Commit 5ca0e05ab57ae1c0aead5a1956bd16a3f80f1d73

Authored by Andrew Shvayka
Committed by GitHub
2 parents 069a51c0 00bd26f1

Merge pull request #4524 from thingsboard/feature/sota

FOTA/SOTA
Showing 66 changed files with 1239 additions and 263 deletions
... ... @@ -63,6 +63,8 @@ CREATE TABLE IF NOT EXISTS firmware (
63 63 id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY,
64 64 created_time bigint NOT NULL,
65 65 tenant_id uuid NOT NULL,
  66 + device_profile_id uuid,
  67 + type varchar(32) NOT NULL,
66 68 title varchar(255) NOT NULL,
67 69 version varchar(255) NOT NULL,
68 70 file_name varchar(255),
... ... @@ -77,10 +79,12 @@ CREATE TABLE IF NOT EXISTS firmware (
77 79 );
78 80
79 81 ALTER TABLE device_profile
80   - ADD COLUMN IF NOT EXISTS firmware_id uuid;
  82 + ADD COLUMN IF NOT EXISTS firmware_id uuid,
  83 + ADD COLUMN IF NOT EXISTS software_id uuid;
81 84
82 85 ALTER TABLE device
83   - ADD COLUMN IF NOT EXISTS firmware_id uuid;
  86 + ADD COLUMN IF NOT EXISTS firmware_id uuid,
  87 + ADD COLUMN IF NOT EXISTS software_id uuid;
84 88
85 89 DO $$
86 90 BEGIN
... ... @@ -90,11 +94,23 @@ DO $$
90 94 FOREIGN KEY (firmware_id) REFERENCES firmware(id);
91 95 END IF;
92 96
  97 + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_software_device_profile') THEN
  98 + ALTER TABLE device_profile
  99 + ADD CONSTRAINT fk_software_device_profile
  100 + FOREIGN KEY (firmware_id) REFERENCES firmware(id);
  101 + END IF;
  102 +
93 103 IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device') THEN
94 104 ALTER TABLE device
95 105 ADD CONSTRAINT fk_firmware_device
96 106 FOREIGN KEY (firmware_id) REFERENCES firmware(id);
97 107 END IF;
  108 +
  109 + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_software_device') THEN
  110 + ALTER TABLE device
  111 + ADD CONSTRAINT fk_software_device
  112 + FOREIGN KEY (firmware_id) REFERENCES firmware(id);
  113 + END IF;
98 114 END;
99 115 $$;
100 116
... ...
... ... @@ -146,12 +146,16 @@ public class DeviceProfileController extends BaseController {
146 146 checkEntity(deviceProfile.getId(), deviceProfile, Resource.DEVICE_PROFILE);
147 147
148 148 boolean isFirmwareChanged = false;
  149 + boolean isSoftwareChanged = false;
149 150
150 151 if (!created) {
151 152 DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(getTenantId(), deviceProfile.getId());
152 153 if (!Objects.equals(deviceProfile.getFirmwareId(), oldDeviceProfile.getFirmwareId())) {
153 154 isFirmwareChanged = true;
154 155 }
  156 + if (!Objects.equals(deviceProfile.getSoftwareId(), oldDeviceProfile.getSoftwareId())) {
  157 + isSoftwareChanged = true;
  158 + }
155 159 }
156 160
157 161 DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile));
... ... @@ -164,9 +168,8 @@ public class DeviceProfileController extends BaseController {
164 168 null,
165 169 created ? ActionType.ADDED : ActionType.UPDATED, null);
166 170
167   - if (isFirmwareChanged) {
168   - firmwareStateService.update(savedDeviceProfile);
169   - }
  171 + firmwareStateService.update(savedDeviceProfile, isFirmwareChanged, isSoftwareChanged);
  172 +
170 173 sendEntityNotificationMsg(getTenantId(), savedDeviceProfile.getId(),
171 174 deviceProfile.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);
172 175 return savedDeviceProfile;
... ...
... ... @@ -35,6 +35,8 @@ import org.thingsboard.server.common.data.Firmware;
35 35 import org.thingsboard.server.common.data.FirmwareInfo;
36 36 import org.thingsboard.server.common.data.audit.ActionType;
37 37 import org.thingsboard.server.common.data.exception.ThingsboardException;
  38 +import org.thingsboard.server.common.data.firmware.FirmwareType;
  39 +import org.thingsboard.server.common.data.id.DeviceProfileId;
38 40 import org.thingsboard.server.common.data.id.FirmwareId;
39 41 import org.thingsboard.server.common.data.page.PageData;
40 42 import org.thingsboard.server.common.data.page.PageLink;
... ... @@ -133,6 +135,8 @@ public class FirmwareController extends BaseController {
133 135 Firmware firmware = new Firmware(firmwareId);
134 136 firmware.setCreatedTime(info.getCreatedTime());
135 137 firmware.setTenantId(getTenantId());
  138 + firmware.setDeviceProfileId(info.getDeviceProfileId());
  139 + firmware.setType(info.getType());
136 140 firmware.setTitle(info.getTitle());
137 141 firmware.setVersion(info.getVersion());
138 142 firmware.setAdditionalInfo(info.getAdditionalInfo());
... ... @@ -175,17 +179,22 @@ public class FirmwareController extends BaseController {
175 179 }
176 180
177 181 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
178   - @RequestMapping(value = "/firmwares/{hasData}", method = RequestMethod.GET)
  182 + @RequestMapping(value = "/firmwares/{deviceProfileId}/{type}/{hasData}", method = RequestMethod.GET)
179 183 @ResponseBody
180   - public PageData<FirmwareInfo> getFirmwares(@PathVariable("hasData") boolean hasData,
  184 + public PageData<FirmwareInfo> getFirmwares(@PathVariable("deviceProfileId") String strDeviceProfileId,
  185 + @PathVariable("type") String strType,
  186 + @PathVariable("hasData") boolean hasData,
181 187 @RequestParam int pageSize,
182 188 @RequestParam int page,
183 189 @RequestParam(required = false) String textSearch,
184 190 @RequestParam(required = false) String sortProperty,
185 191 @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  192 + checkParameter("deviceProfileId", strDeviceProfileId);
  193 + checkParameter("type", strType);
186 194 try {
187 195 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
188   - return checkNotNull(firmwareService.findTenantFirmwaresByTenantIdAndHasData(getTenantId(), hasData, pageLink));
  196 + return checkNotNull(firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(getTenantId(),
  197 + new DeviceProfileId(toUUID(strDeviceProfileId)), FirmwareType.valueOf(strType), hasData, pageLink));
189 198 } catch (Exception e) {
190 199 throw handleException(e);
191 200 }
... ...
... ... @@ -19,13 +19,17 @@ import com.google.common.util.concurrent.FutureCallback;
19 19 import lombok.extern.slf4j.Slf4j;
20 20 import org.springframework.stereotype.Service;
21 21 import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
  22 +import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
22 23 import org.thingsboard.server.common.data.DataConstants;
23 24 import org.thingsboard.server.common.data.Device;
24 25 import org.thingsboard.server.common.data.DeviceProfile;
25 26 import org.thingsboard.server.common.data.FirmwareInfo;
  27 +import org.thingsboard.server.common.data.firmware.FirmwareUtil;
  28 +import org.thingsboard.server.common.data.firmware.FirmwareType;
26 29 import org.thingsboard.server.common.data.id.DeviceId;
27 30 import org.thingsboard.server.common.data.id.FirmwareId;
28 31 import org.thingsboard.server.common.data.id.TenantId;
  32 +import org.thingsboard.server.common.data.kv.AttributeKey;
29 33 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
30 34 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
31 35 import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
... ... @@ -43,37 +47,49 @@ import org.thingsboard.server.queue.TbQueueProducer;
43 47 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
44 48 import org.thingsboard.server.queue.provider.TbCoreQueueFactory;
45 49 import org.thingsboard.server.queue.util.TbCoreComponent;
  50 +import org.thingsboard.server.service.queue.TbClusterService;
46 51
47 52 import javax.annotation.Nullable;
48 53 import java.util.ArrayList;
49   -import java.util.Arrays;
50 54 import java.util.Collections;
  55 +import java.util.HashSet;
51 56 import java.util.List;
  57 +import java.util.Set;
52 58 import java.util.UUID;
53 59 import java.util.function.Consumer;
54   -
55   -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_CHECKSUM;
56   -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_CHECKSUM_ALGORITHM;
57   -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_SIZE;
58   -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_TITLE;
59   -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_VERSION;
  60 +import java.util.function.Function;
  61 +
  62 +import static org.thingsboard.server.common.data.firmware.FirmwareKey.CHECKSUM;
  63 +import static org.thingsboard.server.common.data.firmware.FirmwareKey.CHECKSUM_ALGORITHM;
  64 +import static org.thingsboard.server.common.data.firmware.FirmwareKey.SIZE;
  65 +import static org.thingsboard.server.common.data.firmware.FirmwareKey.STATE;
  66 +import static org.thingsboard.server.common.data.firmware.FirmwareKey.TITLE;
  67 +import static org.thingsboard.server.common.data.firmware.FirmwareKey.TS;
  68 +import static org.thingsboard.server.common.data.firmware.FirmwareKey.VERSION;
  69 +import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getAttributeKey;
  70 +import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getTargetTelemetryKey;
  71 +import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getTelemetryKey;
  72 +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
  73 +import static org.thingsboard.server.common.data.firmware.FirmwareType.SOFTWARE;
60 74
61 75 @Slf4j
62 76 @Service
63 77 @TbCoreComponent
64 78 public class DefaultFirmwareStateService implements FirmwareStateService {
65 79
  80 + private final TbClusterService tbClusterService;
66 81 private final FirmwareService firmwareService;
67 82 private final DeviceService deviceService;
68 83 private final DeviceProfileService deviceProfileService;
69 84 private final RuleEngineTelemetryService telemetryService;
70 85 private final TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> fwStateMsgProducer;
71 86
72   - public DefaultFirmwareStateService(FirmwareService firmwareService,
  87 + public DefaultFirmwareStateService(TbClusterService tbClusterService, FirmwareService firmwareService,
73 88 DeviceService deviceService,
74 89 DeviceProfileService deviceProfileService,
75 90 RuleEngineTelemetryService telemetryService,
76 91 TbCoreQueueFactory coreQueueFactory) {
  92 + this.tbClusterService = tbClusterService;
77 93 this.firmwareService = firmwareService;
78 94 this.deviceService = deviceService;
79 95 this.deviceProfileService = deviceProfileService;
... ... @@ -83,6 +99,11 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
83 99
84 100 @Override
85 101 public void update(Device device, Device oldDevice) {
  102 + updateFirmware(device, oldDevice);
  103 + updateSoftware(device, oldDevice);
  104 + }
  105 +
  106 + private void updateFirmware(Device device, Device oldDevice) {
86 107 FirmwareId newFirmwareId = device.getFirmwareId();
87 108 if (newFirmwareId == null) {
88 109 DeviceProfile newDeviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId());
... ... @@ -97,35 +118,84 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
97 118 }
98 119 if (!newFirmwareId.equals(oldFirmwareId)) {
99 120 // Device was updated and new firmware is different from previous firmware.
100   - send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis());
  121 + send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis(), FIRMWARE);
101 122 }
102 123 } else {
103 124 // Device was updated and new firmware is not set.
104   - remove(device);
  125 + remove(device, FIRMWARE);
105 126 }
106 127 } else if (newFirmwareId != null) {
107 128 // Device was created and firmware is defined.
108   - send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis());
  129 + send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis(), FIRMWARE);
  130 + }
  131 + }
  132 +
  133 + private void updateSoftware(Device device, Device oldDevice) {
  134 + FirmwareId newSoftwareId = device.getSoftwareId();
  135 + if (newSoftwareId == null) {
  136 + DeviceProfile newDeviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId());
  137 + newSoftwareId = newDeviceProfile.getSoftwareId();
  138 + }
  139 + if (oldDevice != null) {
  140 + if (newSoftwareId != null) {
  141 + FirmwareId oldSoftwareId = oldDevice.getSoftwareId();
  142 + if (oldSoftwareId == null) {
  143 + DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(oldDevice.getTenantId(), oldDevice.getDeviceProfileId());
  144 + oldSoftwareId = oldDeviceProfile.getSoftwareId();
  145 + }
  146 + if (!newSoftwareId.equals(oldSoftwareId)) {
  147 + // Device was updated and new firmware is different from previous firmware.
  148 + send(device.getTenantId(), device.getId(), newSoftwareId, System.currentTimeMillis(), SOFTWARE);
  149 + }
  150 + } else {
  151 + // Device was updated and new firmware is not set.
  152 + remove(device, SOFTWARE);
  153 + }
  154 + } else if (newSoftwareId != null) {
  155 + // Device was created and firmware is defined.
  156 + send(device.getTenantId(), device.getId(), newSoftwareId, System.currentTimeMillis(), SOFTWARE);
109 157 }
110 158 }
111 159
112 160 @Override
113   - public void update(DeviceProfile deviceProfile) {
  161 + public void update(DeviceProfile deviceProfile, boolean isFirmwareChanged, boolean isSoftwareChanged) {
114 162 TenantId tenantId = deviceProfile.getTenantId();
115 163
  164 + if (isFirmwareChanged) {
  165 + update(tenantId, deviceProfile, FIRMWARE);
  166 + }
  167 + if (isSoftwareChanged) {
  168 + update(tenantId, deviceProfile, SOFTWARE);
  169 + }
  170 + }
  171 +
  172 + private void update(TenantId tenantId, DeviceProfile deviceProfile, FirmwareType firmwareType) {
  173 + Function<PageLink, PageData<Device>> getDevicesFunction;
116 174 Consumer<Device> updateConsumer;
  175 +
  176 + switch (firmwareType) {
  177 + case FIRMWARE:
  178 + getDevicesFunction = pl -> deviceService.findDevicesByTenantIdAndTypeAndEmptyFirmware(tenantId, deviceProfile.getName(), pl);
  179 + break;
  180 + case SOFTWARE:
  181 + getDevicesFunction = pl -> deviceService.findDevicesByTenantIdAndTypeAndEmptySoftware(tenantId, deviceProfile.getName(), pl);
  182 + break;
  183 + default:
  184 + log.warn("Unsupported firmware type: [{}]", firmwareType);
  185 + return;
  186 + }
  187 +
117 188 if (deviceProfile.getFirmwareId() != null) {
118 189 long ts = System.currentTimeMillis();
119   - updateConsumer = d -> send(d.getTenantId(), d.getId(), deviceProfile.getFirmwareId(), ts);
  190 + updateConsumer = d -> send(d.getTenantId(), d.getId(), deviceProfile.getFirmwareId(), ts, firmwareType);
120 191 } else {
121   - updateConsumer = this::remove;
  192 + updateConsumer = d -> remove(d, firmwareType);
122 193 }
123 194
124 195 PageLink pageLink = new PageLink(100);
125 196 PageData<Device> pageData;
126 197 do {
127   - pageData = deviceService.findDevicesByTenantIdAndTypeAndEmptyFirmware(tenantId, deviceProfile.getName(), pageLink);
128   -
  198 + pageData = getDevicesFunction.apply(pageLink);
129 199 pageData.getData().forEach(updateConsumer);
130 200
131 201 if (pageData.hasNext()) {
... ... @@ -140,16 +210,17 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
140 210 FirmwareId targetFirmwareId = new FirmwareId(new UUID(msg.getFirmwareIdMSB(), msg.getFirmwareIdLSB()));
141 211 DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB()));
142 212 TenantId tenantId = new TenantId(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB()));
  213 + FirmwareType firmwareType = FirmwareType.valueOf(msg.getType());
143 214 long ts = msg.getTs();
144 215
145 216 Device device = deviceService.findDeviceById(tenantId, deviceId);
146 217 if (device == null) {
147 218 log.warn("[{}] [{}] Device was removed during firmware update msg was queued!", tenantId, deviceId);
148 219 } else {
149   - FirmwareId currentFirmwareId = device.getFirmwareId();
150   -
  220 + FirmwareId currentFirmwareId = FirmwareUtil.getFirmwareId(device, firmwareType);
151 221 if (currentFirmwareId == null) {
152   - currentFirmwareId = deviceProfileService.findDeviceProfileById(tenantId, device.getDeviceProfileId()).getFirmwareId();
  222 + DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(tenantId, device.getDeviceProfileId());
  223 + currentFirmwareId = FirmwareUtil.getFirmwareId(deviceProfile, firmwareType);
153 224 }
154 225
155 226 if (targetFirmwareId.equals(currentFirmwareId)) {
... ... @@ -162,7 +233,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
162 233 return isSuccess;
163 234 }
164 235
165   - private void send(TenantId tenantId, DeviceId deviceId, FirmwareId firmwareId, long ts) {
  236 + private void send(TenantId tenantId, DeviceId deviceId, FirmwareId firmwareId, long ts, FirmwareType firmwareType) {
166 237 ToFirmwareStateServiceMsg msg = ToFirmwareStateServiceMsg.newBuilder()
167 238 .setTenantIdMSB(tenantId.getId().getMostSignificantBits())
168 239 .setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
... ... @@ -170,6 +241,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
170 241 .setDeviceIdLSB(deviceId.getId().getLeastSignificantBits())
171 242 .setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits())
172 243 .setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits())
  244 + .setType(firmwareType.name())
173 245 .setTs(ts)
174 246 .build();
175 247
... ... @@ -183,10 +255,10 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
183 255 fwStateMsgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), msg), null);
184 256
185 257 List<TsKvEntry> telemetry = new ArrayList<>();
186   - telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.TARGET_FIRMWARE_TITLE, firmware.getTitle())));
187   - telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.TARGET_FIRMWARE_VERSION, firmware.getVersion())));
188   - telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(DataConstants.TARGET_FIRMWARE_TS, ts)));
189   - telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_STATE, FirmwareUpdateStatus.QUEUED.name())));
  258 + telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), TITLE), firmware.getTitle())));
  259 + telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), VERSION), firmware.getVersion())));
  260 + telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(getTargetTelemetryKey(firmware.getType(), TS), ts)));
  261 + telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), FirmwareUpdateStatus.QUEUED.name())));
190 262
191 263 telemetryService.saveAndNotify(tenantId, deviceId, telemetry, new FutureCallback<>() {
192 264 @Override
... ... @@ -206,7 +278,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
206 278 TenantId tenantId = device.getTenantId();
207 279 DeviceId deviceId = device.getId();
208 280
209   - BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(DataConstants.FIRMWARE_STATE, FirmwareUpdateStatus.INITIATED.name()));
  281 + BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), FirmwareUpdateStatus.INITIATED.name()));
210 282
211 283 telemetryService.saveAndNotify(tenantId, deviceId, Collections.singletonList(status), new FutureCallback<>() {
212 284 @Override
... ... @@ -221,13 +293,12 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
221 293 });
222 294
223 295 List<AttributeKvEntry> attributes = new ArrayList<>();
  296 + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), TITLE), firmware.getTitle())));
  297 + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), VERSION), firmware.getVersion())));
  298 + attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(getAttributeKey(firmware.getType(), SIZE), firmware.getDataSize())));
  299 + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), CHECKSUM_ALGORITHM), firmware.getChecksumAlgorithm())));
  300 + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), CHECKSUM), firmware.getChecksum())));
224 301
225   - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_TITLE, firmware.getTitle())));
226   - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_VERSION, firmware.getVersion())));
227   -
228   - attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(FIRMWARE_SIZE, firmware.getDataSize())));
229   - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_CHECKSUM_ALGORITHM, firmware.getChecksumAlgorithm())));
230   - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_CHECKSUM, firmware.getChecksum())));
231 302 telemetryService.saveAndNotify(tenantId, deviceId, DataConstants.SHARED_SCOPE, attributes, new FutureCallback<>() {
232 303 @Override
233 304 public void onSuccess(@Nullable Void tmp) {
... ... @@ -241,13 +312,15 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
241 312 });
242 313 }
243 314
244   - private void remove(Device device) {
245   - telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE,
246   - Arrays.asList(FIRMWARE_TITLE, FIRMWARE_VERSION, FIRMWARE_SIZE, FIRMWARE_CHECKSUM_ALGORITHM, FIRMWARE_CHECKSUM),
  315 + private void remove(Device device, FirmwareType firmwareType) {
  316 + telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE, FirmwareUtil.getAttributeKeys(firmwareType),
247 317 new FutureCallback<>() {
248 318 @Override
249 319 public void onSuccess(@Nullable Void tmp) {
250 320 log.trace("[{}] Success remove target firmware attributes!", device.getId());
  321 + Set<AttributeKey> keysToNotify = new HashSet<>();
  322 + FirmwareUtil.ALL_FW_ATTRIBUTE_KEYS.forEach(key -> keysToNotify.add(new AttributeKey(DataConstants.SHARED_SCOPE, key)));
  323 + tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete(device.getTenantId(), device.getId(), keysToNotify), null);
251 324 }
252 325
253 326 @Override
... ...
... ... @@ -23,7 +23,7 @@ public interface FirmwareStateService {
23 23
24 24 void update(Device device, Device oldDevice);
25 25
26   - void update(DeviceProfile deviceProfile);
  26 + void update(DeviceProfile deviceProfile, boolean isFirmwareChanged, boolean isSoftwareChanged);
27 27
28 28 boolean process(ToFirmwareStateServiceMsg msg);
29 29
... ...
... ... @@ -41,6 +41,8 @@ import org.thingsboard.server.common.data.TenantProfile;
41 41 import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials;
42 42 import org.thingsboard.server.common.data.device.credentials.ProvisionDeviceCredentialsData;
43 43 import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileCredentials;
  44 +import org.thingsboard.server.common.data.firmware.FirmwareType;
  45 +import org.thingsboard.server.common.data.firmware.FirmwareUtil;
44 46 import org.thingsboard.server.common.data.id.CustomerId;
45 47 import org.thingsboard.server.common.data.id.DeviceId;
46 48 import org.thingsboard.server.common.data.id.DeviceProfileId;
... ... @@ -512,16 +514,17 @@ public class DefaultTransportApiService implements TransportApiService {
512 514 private ListenableFuture<TransportApiResponseMsg> handle(TransportProtos.GetFirmwareRequestMsg requestMsg) {
513 515 TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB()));
514 516 DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB()));
  517 + FirmwareType firmwareType = FirmwareType.valueOf(requestMsg.getType());
515 518 Device device = deviceService.findDeviceById(tenantId, deviceId);
516 519
517 520 if (device == null) {
518 521 return getEmptyTransportApiResponseFuture();
519 522 }
520 523
521   - FirmwareId firmwareId = device.getFirmwareId();
522   -
  524 + FirmwareId firmwareId = FirmwareUtil.getFirmwareId(device, firmwareType);
523 525 if (firmwareId == null) {
524   - firmwareId = deviceProfileCache.find(device.getDeviceProfileId()).getFirmwareId();
  526 + DeviceProfile deviceProfile = deviceProfileCache.find(device.getDeviceProfileId());
  527 + firmwareId = FirmwareUtil.getFirmwareId(deviceProfile, firmwareType);
525 528 }
526 529
527 530 TransportProtos.GetFirmwareResponseMsg.Builder builder = TransportProtos.GetFirmwareResponseMsg.newBuilder();
... ... @@ -537,6 +540,7 @@ public class DefaultTransportApiService implements TransportApiService {
537 540 builder.setResponseStatus(TransportProtos.ResponseStatus.SUCCESS);
538 541 builder.setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits());
539 542 builder.setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits());
  543 + builder.setType(firmwareInfo.getType().name());
540 544 builder.setTitle(firmwareInfo.getTitle());
541 545 builder.setVersion(firmwareInfo.getVersion());
542 546 builder.setFileName(firmwareInfo.getFileName());
... ...
... ... @@ -24,10 +24,13 @@ import org.springframework.mock.web.MockMultipartFile;
24 24 import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;
25 25 import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
26 26 import org.thingsboard.common.util.JacksonUtil;
  27 +import org.thingsboard.server.common.data.DeviceProfile;
27 28 import org.thingsboard.server.common.data.Firmware;
28 29 import org.thingsboard.server.common.data.FirmwareInfo;
29 30 import org.thingsboard.server.common.data.Tenant;
30 31 import org.thingsboard.server.common.data.User;
  32 +import org.thingsboard.server.common.data.firmware.FirmwareType;
  33 +import org.thingsboard.server.common.data.id.DeviceProfileId;
31 34 import org.thingsboard.server.common.data.page.PageData;
32 35 import org.thingsboard.server.common.data.page.PageLink;
33 36 import org.thingsboard.server.common.data.security.Authority;
... ... @@ -38,6 +41,7 @@ import java.util.Collections;
38 41 import java.util.List;
39 42
40 43 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  44 +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
41 45
42 46 public abstract class BaseFirmwareControllerTest extends AbstractControllerTest {
43 47
... ... @@ -53,6 +57,7 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
53 57
54 58 private Tenant savedTenant;
55 59 private User tenantAdmin;
  60 + private DeviceProfileId deviceProfileId;
56 61
57 62 @Before
58 63 public void beforeTest() throws Exception {
... ... @@ -71,6 +76,11 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
71 76 tenantAdmin.setLastName("Downs");
72 77
73 78 tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
  79 +
  80 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null);
  81 + DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
  82 + Assert.assertNotNull(savedDeviceProfile);
  83 + deviceProfileId = savedDeviceProfile.getId();
74 84 }
75 85
76 86 @After
... ... @@ -84,6 +94,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
84 94 @Test
85 95 public void testSaveFirmware() throws Exception {
86 96 FirmwareInfo firmwareInfo = new FirmwareInfo();
  97 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  98 + firmwareInfo.setType(FIRMWARE);
87 99 firmwareInfo.setTitle(TITLE);
88 100 firmwareInfo.setVersion(VERSION);
89 101
... ... @@ -107,6 +119,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
107 119 @Test
108 120 public void testSaveFirmwareData() throws Exception {
109 121 FirmwareInfo firmwareInfo = new FirmwareInfo();
  122 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  123 + firmwareInfo.setType(FIRMWARE);
110 124 firmwareInfo.setTitle(TITLE);
111 125 firmwareInfo.setVersion(VERSION);
112 126
... ... @@ -137,6 +151,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
137 151 @Test
138 152 public void testUpdateFirmwareFromDifferentTenant() throws Exception {
139 153 FirmwareInfo firmwareInfo = new FirmwareInfo();
  154 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  155 + firmwareInfo.setType(FIRMWARE);
140 156 firmwareInfo.setTitle(TITLE);
141 157 firmwareInfo.setVersion(VERSION);
142 158
... ... @@ -150,6 +166,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
150 166 @Test
151 167 public void testFindFirmwareInfoById() throws Exception {
152 168 FirmwareInfo firmwareInfo = new FirmwareInfo();
  169 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  170 + firmwareInfo.setType(FIRMWARE);
153 171 firmwareInfo.setTitle(TITLE);
154 172 firmwareInfo.setVersion(VERSION);
155 173
... ... @@ -163,6 +181,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
163 181 @Test
164 182 public void testFindFirmwareById() throws Exception {
165 183 FirmwareInfo firmwareInfo = new FirmwareInfo();
  184 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  185 + firmwareInfo.setType(FIRMWARE);
166 186 firmwareInfo.setTitle(TITLE);
167 187 firmwareInfo.setVersion(VERSION);
168 188
... ... @@ -180,6 +200,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
180 200 @Test
181 201 public void testDeleteFirmware() throws Exception {
182 202 FirmwareInfo firmwareInfo = new FirmwareInfo();
  203 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  204 + firmwareInfo.setType(FIRMWARE);
183 205 firmwareInfo.setTitle(TITLE);
184 206 firmwareInfo.setVersion(VERSION);
185 207
... ... @@ -197,6 +219,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
197 219 List<FirmwareInfo> firmwares = new ArrayList<>();
198 220 for (int i = 0; i < 165; i++) {
199 221 FirmwareInfo firmwareInfo = new FirmwareInfo();
  222 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  223 + firmwareInfo.setType(FIRMWARE);
200 224 firmwareInfo.setTitle(TITLE);
201 225 firmwareInfo.setVersion(VERSION + i);
202 226
... ... @@ -238,6 +262,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
238 262
239 263 for (int i = 0; i < 165; i++) {
240 264 FirmwareInfo firmwareInfo = new FirmwareInfo();
  265 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  266 + firmwareInfo.setType(FIRMWARE);
241 267 firmwareInfo.setTitle(TITLE);
242 268 firmwareInfo.setVersion(VERSION + i);
243 269
... ... @@ -257,7 +283,7 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
257 283 PageLink pageLink = new PageLink(24);
258 284 PageData<FirmwareInfo> pageData;
259 285 do {
260   - pageData = doGetTypedWithPageLink("/api/firmwares/true?",
  286 + pageData = doGetTypedWithPageLink("/api/firmwares/" + deviceProfileId.toString() + "/FIRMWARE/true?",
261 287 new TypeReference<>() {
262 288 }, pageLink);
263 289 loadedFirmwaresWithData.addAll(pageData.getData());
... ... @@ -269,7 +295,7 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
269 295 List<FirmwareInfo> loadedFirmwaresWithoutData = new ArrayList<>();
270 296 pageLink = new PageLink(24);
271 297 do {
272   - pageData = doGetTypedWithPageLink("/api/firmwares/false?",
  298 + pageData = doGetTypedWithPageLink("/api/firmwares/" + deviceProfileId.toString() + "/FIRMWARE/false?",
273 299 new TypeReference<>() {
274 300 }, pageLink);
275 301 loadedFirmwaresWithoutData.addAll(pageData.getData());
... ...
... ... @@ -65,6 +65,8 @@ public interface DeviceService {
65 65
66 66 PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink);
67 67
  68 + PageData<Device> findDevicesByTenantIdAndTypeAndEmptySoftware(TenantId tenantId, String type, PageLink pageLink);
  69 +
68 70 PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink);
69 71
70 72 PageData<DeviceInfo> findDeviceInfosByTenantIdAndDeviceProfileId(TenantId tenantId, DeviceProfileId deviceProfileId, PageLink pageLink);
... ...
... ... @@ -18,6 +18,8 @@ package org.thingsboard.server.dao.firmware;
18 18 import com.google.common.util.concurrent.ListenableFuture;
19 19 import org.thingsboard.server.common.data.Firmware;
20 20 import org.thingsboard.server.common.data.FirmwareInfo;
  21 +import org.thingsboard.server.common.data.firmware.FirmwareType;
  22 +import org.thingsboard.server.common.data.id.DeviceProfileId;
21 23 import org.thingsboard.server.common.data.id.FirmwareId;
22 24 import org.thingsboard.server.common.data.id.TenantId;
23 25 import org.thingsboard.server.common.data.page.PageData;
... ... @@ -37,7 +39,7 @@ public interface FirmwareService {
37 39
38 40 PageData<FirmwareInfo> findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink);
39 41
40   - PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink);
  42 + PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink);
41 43
42 44 void deleteFirmware(TenantId tenantId, FirmwareId firmwareId);
43 45
... ...
... ... @@ -93,22 +93,26 @@ public class DataConstants {
93 93 public static final String USERNAME = "username";
94 94 public static final String PASSWORD = "password";
95 95
96   - //firmware
97   - //telemetry
98   - public static final String CURRENT_FIRMWARE_TITLE = "current_fw_title";
99   - public static final String CURRENT_FIRMWARE_VERSION = "current_fw_version";
100   - public static final String TARGET_FIRMWARE_TITLE = "target_fw_title";
101   - public static final String TARGET_FIRMWARE_VERSION = "target_fw_version";
102   - public static final String TARGET_FIRMWARE_TS = "target_fw_ts";
103   - public static final String FIRMWARE_STATE = "fw_state";
104   -
105   - //attributes
106   - //telemetry
107   - public static final String FIRMWARE_TITLE = "fw_title";
108   - public static final String FIRMWARE_VERSION = "fw_version";
109   - public static final String FIRMWARE_SIZE = "fw_size";
110   - public static final String FIRMWARE_CHECKSUM = "fw_checksum";
111   - public static final String FIRMWARE_CHECKSUM_ALGORITHM = "fw_checksum_algorithm";
  96 +//<<<<<<< HEAD
  97 +//=======
  98 +// //firmware
  99 +// //telemetry
  100 +// public static final String CURRENT_FIRMWARE_TITLE = "current_fw_title";
  101 +// public static final String CURRENT_FIRMWARE_VERSION = "current_fw_version";
  102 +// public static final String TARGET_FIRMWARE_TITLE = "target_fw_title";
  103 +// public static final String TARGET_FIRMWARE_VERSION = "target_fw_version";
  104 +// public static final String TARGET_FIRMWARE_TS = "target_fw_ts";
  105 +// public static final String FIRMWARE_STATE = "fw_state";
  106 +//
  107 +// //attributes
  108 +// //telemetry
  109 +// public static final String FIRMWARE_TITLE = "fw_title";
  110 +// public static final String FIRMWARE_VERSION = "fw_version";
  111 +// public static final String FIRMWARE_SIZE = "fw_size";
  112 +// public static final String FIRMWARE_CHECKSUM = "fw_checksum";
  113 +// public static final String FIRMWARE_CHECKSUM_ALGORITHM = "fw_checksum_algorithm";
  114 +//>>>>>>> origin/master
112 115 public static final String EDGE_MSG_SOURCE = "edge";
113 116 public static final String MSG_SOURCE_KEY = "source";
  117 +
114 118 }
... ...
... ... @@ -32,7 +32,7 @@ import java.io.IOException;
32 32
33 33 @EqualsAndHashCode(callSuper = true)
34 34 @Slf4j
35   -public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implements HasName, HasTenantId, HasCustomerId {
  35 +public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implements HasName, HasTenantId, HasCustomerId, HasFirmware {
36 36
37 37 private static final long serialVersionUID = 2807343040519543363L;
38 38
... ... @@ -50,6 +50,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
50 50 private byte[] deviceDataBytes;
51 51
52 52 private FirmwareId firmwareId;
  53 + private FirmwareId softwareId;
53 54
54 55 public Device() {
55 56 super();
... ... @@ -69,6 +70,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
69 70 this.deviceProfileId = device.getDeviceProfileId();
70 71 this.setDeviceData(device.getDeviceData());
71 72 this.firmwareId = device.getFirmwareId();
  73 + this.softwareId = device.getSoftwareId();
72 74 }
73 75
74 76 public Device updateDevice(Device device) {
... ... @@ -79,6 +81,8 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
79 81 this.label = device.getLabel();
80 82 this.deviceProfileId = device.getDeviceProfileId();
81 83 this.setDeviceData(device.getDeviceData());
  84 + this.setFirmwareId(device.getFirmwareId());
  85 + this.setSoftwareId(device.getSoftwareId());
82 86 return this;
83 87 }
84 88
... ... @@ -171,6 +175,14 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
171 175 this.firmwareId = firmwareId;
172 176 }
173 177
  178 + public FirmwareId getSoftwareId() {
  179 + return softwareId;
  180 + }
  181 +
  182 + public void setSoftwareId(FirmwareId softwareId) {
  183 + this.softwareId = softwareId;
  184 + }
  185 +
174 186 @Override
175 187 public String toString() {
176 188 StringBuilder builder = new StringBuilder();
... ...
... ... @@ -36,7 +36,7 @@ import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalIn
36 36 @Data
37 37 @EqualsAndHashCode(callSuper = true)
38 38 @Slf4j
39   -public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements HasName, HasTenantId {
  39 +public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements HasName, HasTenantId, HasFirmware {
40 40
41 41 private TenantId tenantId;
42 42 @NoXss
... ... @@ -59,6 +59,8 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
59 59
60 60 private FirmwareId firmwareId;
61 61
  62 + private FirmwareId softwareId;
  63 +
62 64 public DeviceProfile() {
63 65 super();
64 66 }
... ... @@ -77,6 +79,8 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
77 79 this.defaultQueueName = deviceProfile.getDefaultQueueName();
78 80 this.setProfileData(deviceProfile.getProfileData());
79 81 this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey();
  82 + this.firmwareId = deviceProfile.getFirmwareId();
  83 + this.softwareId = deviceProfile.getSoftwareId();
80 84 }
81 85
82 86 @Override
... ...
... ... @@ -19,6 +19,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
19 19 import lombok.Data;
20 20 import lombok.EqualsAndHashCode;
21 21 import lombok.extern.slf4j.Slf4j;
  22 +import org.thingsboard.server.common.data.firmware.FirmwareType;
  23 +import org.thingsboard.server.common.data.id.DeviceProfileId;
22 24 import org.thingsboard.server.common.data.id.FirmwareId;
23 25 import org.thingsboard.server.common.data.id.TenantId;
24 26
... ... @@ -30,6 +32,8 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId>
30 32 private static final long serialVersionUID = 3168391583570815419L;
31 33
32 34 private TenantId tenantId;
  35 + private DeviceProfileId deviceProfileId;
  36 + private FirmwareType type;
33 37 private String title;
34 38 private String version;
35 39 private boolean hasData;
... ... @@ -51,6 +55,8 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId>
51 55 public FirmwareInfo(FirmwareInfo firmwareInfo) {
52 56 super(firmwareInfo);
53 57 this.tenantId = firmwareInfo.getTenantId();
  58 + this.deviceProfileId = firmwareInfo.getDeviceProfileId();
  59 + this.type = firmwareInfo.getType();
54 60 this.title = firmwareInfo.getTitle();
55 61 this.version = firmwareInfo.getVersion();
56 62 this.hasData = firmwareInfo.isHasData();
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data;
  17 +
  18 +import org.thingsboard.server.common.data.id.FirmwareId;
  19 +
  20 +public interface HasFirmware {
  21 +
  22 + FirmwareId getFirmwareId();
  23 +
  24 + FirmwareId getSoftwareId();
  25 +}
... ...
... ... @@ -31,6 +31,7 @@ public class MqttTopics {
31 31 private static final String SUB_TOPIC = "+";
32 32 private static final String PROVISION = "/provision";
33 33 private static final String FIRMWARE = "/fw";
  34 + private static final String SOFTWARE = "/sw";
34 35 private static final String CHUNK = "/chunk/";
35 36 private static final String ERROR = "/error";
36 37
... ... @@ -75,9 +76,17 @@ public class MqttTopics {
75 76 // v2 topics
76 77 public static final String BASE_DEVICE_API_TOPIC_V2 = "v2";
77 78
78   - public static final String DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + RESPONSE + "/";
79   - public static final String DEVICE_FIRMWARE_RESPONSES_TOPIC = DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX + SUB_TOPIC + CHUNK + SUB_TOPIC;
  79 + public static final String REQUEST_ID_PATTERN = "(?<requestId>\\d+)";
  80 + public static final String CHUNK_PATTERN = "(?<chunk>\\d+)";
  81 +
  82 + public static final String DEVICE_FIRMWARE_REQUEST_TOPIC_PATTERN = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + REQUEST + "/" + REQUEST_ID_PATTERN + CHUNK + CHUNK_PATTERN;
  83 + public static final String DEVICE_FIRMWARE_RESPONSES_TOPIC = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + RESPONSE + "/" + SUB_TOPIC + CHUNK + SUB_TOPIC;
80 84 public static final String DEVICE_FIRMWARE_ERROR_TOPIC = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + ERROR;
  85 + public static final String DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT = BASE_DEVICE_API_TOPIC_V2 + "%s" + RESPONSE + "/"+ "%s" + CHUNK + "%d";
  86 +
  87 + public static final String DEVICE_SOFTWARE_REQUEST_TOPIC_PATTERN = BASE_DEVICE_API_TOPIC_V2 + SOFTWARE + REQUEST + "/" + REQUEST_ID_PATTERN + CHUNK + CHUNK_PATTERN;
  88 + public static final String DEVICE_SOFTWARE_RESPONSES_TOPIC = BASE_DEVICE_API_TOPIC_V2 + SOFTWARE + RESPONSE + "/" + SUB_TOPIC + CHUNK + SUB_TOPIC;
  89 + public static final String DEVICE_SOFTWARE_ERROR_TOPIC = BASE_DEVICE_API_TOPIC_V2 + SOFTWARE + ERROR;
81 90
82 91 private MqttTopics() {
83 92 }
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.firmware;
  17 +
  18 +import lombok.Getter;
  19 +
  20 +public enum FirmwareKey {
  21 +
  22 + TITLE("title"), VERSION("version"), TS("ts"), STATE("state"), SIZE("size"), CHECKSUM("checksum"), CHECKSUM_ALGORITHM("checksum_algorithm");
  23 +
  24 + @Getter
  25 + private final String value;
  26 +
  27 + FirmwareKey(String value) {
  28 + this.value = value;
  29 + }
  30 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.firmware;
  17 +
  18 +import lombok.Getter;
  19 +
  20 +public enum FirmwareType {
  21 +
  22 + FIRMWARE("fw"), SOFTWARE("sw");
  23 +
  24 + @Getter
  25 + private final String keyPrefix;
  26 +
  27 + FirmwareType(String keyPrefix) {
  28 + this.keyPrefix = keyPrefix;
  29 + }
  30 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.firmware;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.thingsboard.server.common.data.HasFirmware;
  20 +import org.thingsboard.server.common.data.id.FirmwareId;
  21 +
  22 +import java.util.ArrayList;
  23 +import java.util.Collections;
  24 +import java.util.List;
  25 +
  26 +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
  27 +import static org.thingsboard.server.common.data.firmware.FirmwareType.SOFTWARE;
  28 +
  29 +@Slf4j
  30 +public class FirmwareUtil {
  31 +
  32 + public static final List<String> ALL_FW_ATTRIBUTE_KEYS;
  33 +
  34 + public static final List<String> ALL_SW_ATTRIBUTE_KEYS;
  35 +
  36 + static {
  37 + ALL_FW_ATTRIBUTE_KEYS = new ArrayList<>();
  38 + for (FirmwareKey key : FirmwareKey.values()) {
  39 + ALL_FW_ATTRIBUTE_KEYS.add(getAttributeKey(FIRMWARE, key));
  40 +
  41 + }
  42 +
  43 + ALL_SW_ATTRIBUTE_KEYS = new ArrayList<>();
  44 + for (FirmwareKey key : FirmwareKey.values()) {
  45 + ALL_SW_ATTRIBUTE_KEYS.add(getAttributeKey(SOFTWARE, key));
  46 +
  47 + }
  48 + }
  49 +
  50 + public static List<String> getAttributeKeys(FirmwareType firmwareType) {
  51 + switch (firmwareType) {
  52 + case FIRMWARE:
  53 + return ALL_FW_ATTRIBUTE_KEYS;
  54 + case SOFTWARE:
  55 + return ALL_SW_ATTRIBUTE_KEYS;
  56 + }
  57 + return Collections.emptyList();
  58 + }
  59 +
  60 + public static String getAttributeKey(FirmwareType type, FirmwareKey key) {
  61 + return type.getKeyPrefix() + "_" + key.getValue();
  62 + }
  63 +
  64 + public static String getTargetTelemetryKey(FirmwareType type, FirmwareKey key) {
  65 + return getTelemetryKey("target_", type, key);
  66 + }
  67 +
  68 + public static String getCurrentTelemetryKey(FirmwareType type, FirmwareKey key) {
  69 + return getTelemetryKey("current_", type, key);
  70 + }
  71 +
  72 + private static String getTelemetryKey(String prefix, FirmwareType type, FirmwareKey key) {
  73 + return prefix + type.getKeyPrefix() + "_" + key.getValue();
  74 + }
  75 +
  76 + public static String getTelemetryKey(FirmwareType type, FirmwareKey key) {
  77 + return type.getKeyPrefix() + "_" + key.getValue();
  78 + }
  79 +
  80 + public static FirmwareId getFirmwareId(HasFirmware entity, FirmwareType firmwareType) {
  81 + switch (firmwareType) {
  82 + case FIRMWARE:
  83 + return entity.getFirmwareId();
  84 + case SOFTWARE:
  85 + return entity.getSoftwareId();
  86 + default:
  87 + log.warn("Unsupported firmware type: [{}]", firmwareType);
  88 + return null;
  89 + }
  90 + }
  91 +}
... ...
... ... @@ -16,5 +16,5 @@
16 16 package org.thingsboard.server.common.msg.session;
17 17
18 18 public enum FeatureType {
19   - ATTRIBUTES, TELEMETRY, RPC, CLAIM, PROVISION, FIRMWARE
  19 + ATTRIBUTES, TELEMETRY, RPC, CLAIM, PROVISION, FIRMWARE, SOFTWARE
20 20 }
... ...
... ... @@ -32,7 +32,8 @@ public enum SessionMsgType {
32 32
33 33 CLAIM_REQUEST(),
34 34
35   - GET_FIRMWARE_REQUEST;
  35 + GET_FIRMWARE_REQUEST,
  36 + GET_SOFTWARE_REQUEST;
36 37
37 38 private final boolean requiresRulesProcessing;
38 39
... ...
... ... @@ -388,16 +388,18 @@ message GetFirmwareRequestMsg {
388 388 int64 deviceIdLSB = 2;
389 389 int64 tenantIdMSB = 3;
390 390 int64 tenantIdLSB = 4;
  391 + string type = 5;
391 392 }
392 393
393 394 message GetFirmwareResponseMsg {
394 395 ResponseStatus responseStatus = 1;
395 396 int64 firmwareIdMSB = 2;
396 397 int64 firmwareIdLSB = 3;
397   - string title = 4;
398   - string version = 5;
399   - string contentType = 6;
400   - string fileName = 7;
  398 + string type = 4;
  399 + string title = 5;
  400 + string version = 6;
  401 + string contentType = 7;
  402 + string fileName = 8;
401 403 }
402 404
403 405 //Used to report session state to tb-Service and persist this state in the cache on the tb-Service level.
... ... @@ -711,4 +713,5 @@ message ToFirmwareStateServiceMsg {
711 713 int64 deviceIdLSB = 5;
712 714 int64 firmwareIdMSB = 6;
713 715 int64 firmwareIdLSB = 7;
  716 + string type = 8;
714 717 }
... ...
... ... @@ -43,6 +43,7 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportC
43 43 import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration;
44 44 import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
45 45 import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
  46 +import org.thingsboard.server.common.data.firmware.FirmwareType;
46 47 import org.thingsboard.server.common.data.security.DeviceTokenCredentials;
47 48 import org.thingsboard.server.common.msg.session.FeatureType;
48 49 import org.thingsboard.server.common.msg.session.SessionMsgType;
... ... @@ -122,6 +123,8 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
122 123 processRequest(exchange, SessionMsgType.GET_ATTRIBUTES_REQUEST);
123 124 } else if (featureType.get() == FeatureType.FIRMWARE) {
124 125 processRequest(exchange, SessionMsgType.GET_FIRMWARE_REQUEST);
  126 + } else if (featureType.get() == FeatureType.SOFTWARE) {
  127 + processRequest(exchange, SessionMsgType.GET_SOFTWARE_REQUEST);
125 128 } else {
126 129 log.trace("Invalid feature type parameter");
127 130 exchange.respond(CoAP.ResponseCode.BAD_REQUEST);
... ... @@ -326,12 +329,10 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
326 329 new CoapNoOpCallback(exchange));
327 330 break;
328 331 case GET_FIRMWARE_REQUEST:
329   - TransportProtos.GetFirmwareRequestMsg requestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder()
330   - .setTenantIdMSB(sessionInfo.getTenantIdMSB())
331   - .setTenantIdLSB(sessionInfo.getTenantIdLSB())
332   - .setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
333   - .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()).build();
334   - transportContext.getTransportService().process(sessionInfo, requestMsg, new FirmwareCallback(exchange));
  332 + getFirmwareCallback(sessionInfo, exchange, FirmwareType.FIRMWARE);
  333 + break;
  334 + case GET_SOFTWARE_REQUEST:
  335 + getFirmwareCallback(sessionInfo, exchange, FirmwareType.SOFTWARE);
335 336 break;
336 337 }
337 338 } catch (AdaptorException e) {
... ... @@ -340,6 +341,16 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
340 341 }
341 342 }
342 343
  344 + private void getFirmwareCallback(TransportProtos.SessionInfoProto sessionInfo, CoapExchange exchange, FirmwareType firmwareType) {
  345 + TransportProtos.GetFirmwareRequestMsg requestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder()
  346 + .setTenantIdMSB(sessionInfo.getTenantIdMSB())
  347 + .setTenantIdLSB(sessionInfo.getTenantIdLSB())
  348 + .setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
  349 + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
  350 + .setType(firmwareType.name()).build();
  351 + transportContext.getTransportService().process(sessionInfo, requestMsg, new FirmwareCallback(exchange));
  352 + }
  353 +
343 354 private TransportProtos.SessionInfoProto lookupAsyncSessionInfo(String token) {
344 355 tokenToNotificationCounterMap.remove(token);
345 356 return tokenToSessionIdMap.remove(token);
... ...
... ... @@ -34,6 +34,7 @@ import org.springframework.web.bind.annotation.RequestParam;
34 34 import org.springframework.web.bind.annotation.RestController;
35 35 import org.springframework.web.context.request.async.DeferredResult;
36 36 import org.thingsboard.server.common.data.DeviceTransportType;
  37 +import org.thingsboard.server.common.data.firmware.FirmwareType;
37 38 import org.thingsboard.server.common.data.TbTransportService;
38 39 import org.thingsboard.server.common.data.id.DeviceId;
39 40 import org.thingsboard.server.common.transport.SessionMsgListener;
... ... @@ -210,8 +211,29 @@ public class DeviceApiController implements TbTransportService {
210 211 public DeferredResult<ResponseEntity> getFirmware(@PathVariable("deviceToken") String deviceToken,
211 212 @RequestParam(value = "title") String title,
212 213 @RequestParam(value = "version") String version,
213   - @RequestParam(value = "chunkSize", required = false, defaultValue = "0") int chunkSize,
  214 + @RequestParam(value = "size", required = false, defaultValue = "0") int size,
214 215 @RequestParam(value = "chunk", required = false, defaultValue = "0") int chunk) {
  216 + return getFirmwareCallback(deviceToken, title, version, size, chunk, FirmwareType.FIRMWARE);
  217 + }
  218 +
  219 + @RequestMapping(value = "/{deviceToken}/software", method = RequestMethod.GET)
  220 + public DeferredResult<ResponseEntity> getSoftware(@PathVariable("deviceToken") String deviceToken,
  221 + @RequestParam(value = "title") String title,
  222 + @RequestParam(value = "version") String version,
  223 + @RequestParam(value = "size", required = false, defaultValue = "0") int size,
  224 + @RequestParam(value = "chunk", required = false, defaultValue = "0") int chunk) {
  225 + return getFirmwareCallback(deviceToken, title, version, size, chunk, FirmwareType.SOFTWARE);
  226 + }
  227 +
  228 + @RequestMapping(value = "/provision", method = RequestMethod.POST)
  229 + public DeferredResult<ResponseEntity> provisionDevice(@RequestBody String json, HttpServletRequest httpRequest) {
  230 + DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>();
  231 + transportContext.getTransportService().process(JsonConverter.convertToProvisionRequestMsg(json),
  232 + new DeviceProvisionCallback(responseWriter));
  233 + return responseWriter;
  234 + }
  235 +
  236 + private DeferredResult<ResponseEntity> getFirmwareCallback(String deviceToken, String title, String version, int size, int chunk, FirmwareType firmwareType) {
215 237 DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>();
216 238 transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(),
217 239 new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> {
... ... @@ -219,20 +241,13 @@ public class DeviceApiController implements TbTransportService {
219 241 .setTenantIdMSB(sessionInfo.getTenantIdMSB())
220 242 .setTenantIdLSB(sessionInfo.getTenantIdLSB())
221 243 .setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
222   - .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()).build();
223   - transportContext.getTransportService().process(sessionInfo, requestMsg, new GetFirmwareCallback(responseWriter, title, version, chunkSize, chunk));
  244 + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
  245 + .setType(firmwareType.name()).build();
  246 + transportContext.getTransportService().process(sessionInfo, requestMsg, new GetFirmwareCallback(responseWriter, title, version, size, chunk));
224 247 }));
225 248 return responseWriter;
226 249 }
227 250
228   - @RequestMapping(value = "/provision", method = RequestMethod.POST)
229   - public DeferredResult<ResponseEntity> provisionDevice(@RequestBody String json, HttpServletRequest httpRequest) {
230   - DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>();
231   - transportContext.getTransportService().process(JsonConverter.convertToProvisionRequestMsg(json),
232   - new DeviceProvisionCallback(responseWriter));
233   - return responseWriter;
234   - }
235   -
236 251 private static class DeviceAuthCallback implements TransportServiceCallback<ValidateDeviceCredentialsResponse> {
237 252 private final TransportContext transportContext;
238 253 private final DeferredResult<ResponseEntity> responseWriter;
... ...
... ... @@ -41,6 +41,9 @@ import org.thingsboard.common.util.JacksonUtil;
41 41 import org.thingsboard.server.cache.firmware.FirmwareDataCache;
42 42 import org.thingsboard.server.common.data.Device;
43 43 import org.thingsboard.server.common.data.DeviceProfile;
  44 +import org.thingsboard.server.common.data.firmware.FirmwareKey;
  45 +import org.thingsboard.server.common.data.firmware.FirmwareType;
  46 +import org.thingsboard.server.common.data.firmware.FirmwareUtil;
44 47 import org.thingsboard.server.common.data.id.FirmwareId;
45 48 import org.thingsboard.server.common.transport.TransportService;
46 49 import org.thingsboard.server.common.transport.TransportServiceCallback;
... ... @@ -79,7 +82,6 @@ import java.util.stream.Collectors;
79 82
80 83 import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST;
81 84 import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION;
82   -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_VERSION;
83 85 import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY;
84 86 import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
85 87 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.CLIENT_NOT_AUTHORIZED;
... ... @@ -332,7 +334,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
332 334 String pathName = tsKvProto.getKv().getKey();
333 335 String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName);
334 336 Object valueNew = this.lwM2mTransportContextServer.getValueFromKvProto(tsKvProto.getKv());
335   - if (FIRMWARE_VERSION.equals(pathName) && !valueNew.equals(lwM2MClient.getFrUpdate().getCurrentFwVersion())) {
  337 + if (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFrUpdate().getCurrentFwVersion())) {
336 338 this.getInfoFirmwareUpdate(lwM2MClient);
337 339 }
338 340 if (pathIdVer != null) {
... ... @@ -358,7 +360,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
358 360 msg.getSharedUpdatedList().forEach(tsKvProto -> {
359 361 String pathName = tsKvProto.getKv().getKey();
360 362 Object valueNew = this.lwM2mTransportContextServer.getValueFromKvProto(tsKvProto.getKv());
361   - if (FIRMWARE_VERSION.equals(pathName) && !valueNew.equals(lwM2MClient.getFrUpdate().getCurrentFwVersion())) {
  363 + if (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFrUpdate().getCurrentFwVersion())) {
362 364 lwM2MClient.getFrUpdate().setCurrentFwVersion((String) valueNew);
363 365 }
364 366 });
... ...
... ... @@ -47,6 +47,7 @@ import org.thingsboard.server.common.data.DeviceProfile;
47 47 import org.thingsboard.server.common.data.DeviceTransportType;
48 48 import org.thingsboard.server.common.data.TransportPayloadType;
49 49 import org.thingsboard.server.common.data.device.profile.MqttTopics;
  50 +import org.thingsboard.server.common.data.firmware.FirmwareType;
50 51 import org.thingsboard.server.common.data.id.FirmwareId;
51 52 import org.thingsboard.server.common.msg.EncryptionUtil;
52 53 import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
... ... @@ -59,6 +60,7 @@ import org.thingsboard.server.common.transport.auth.TransportDeviceInfo;
59 60 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
60 61 import org.thingsboard.server.common.transport.service.DefaultTransportService;
61 62 import org.thingsboard.server.common.transport.service.SessionMetaData;
  63 +import org.thingsboard.server.common.transport.util.SslUtil;
62 64 import org.thingsboard.server.gen.transport.TransportProtos;
63 65 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg;
64 66 import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
... ... @@ -68,7 +70,6 @@ import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor;
68 70 import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx;
69 71 import org.thingsboard.server.transport.mqtt.session.GatewaySessionHandler;
70 72 import org.thingsboard.server.transport.mqtt.session.MqttTopicMatcher;
71   -import org.thingsboard.server.common.transport.util.SslUtil;
72 73
73 74 import javax.net.ssl.SSLPeerUnverifiedException;
74 75 import java.io.IOException;
... ... @@ -97,6 +98,8 @@ import static io.netty.handler.codec.mqtt.MqttMessageType.UNSUBACK;
97 98 import static io.netty.handler.codec.mqtt.MqttQoS.AT_LEAST_ONCE;
98 99 import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE;
99 100 import static io.netty.handler.codec.mqtt.MqttQoS.FAILURE;
  101 +import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_FIRMWARE_REQUEST_TOPIC_PATTERN;
  102 +import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_SOFTWARE_REQUEST_TOPIC_PATTERN;
100 103
101 104 /**
102 105 * @author Andrew Shvayka
... ... @@ -104,7 +107,9 @@ import static io.netty.handler.codec.mqtt.MqttQoS.FAILURE;
104 107 @Slf4j
105 108 public class MqttTransportHandler extends ChannelInboundHandlerAdapter implements GenericFutureListener<Future<? super Void>>, SessionMsgListener {
106 109
107   - private static final Pattern FW_PATTERN = Pattern.compile("v2/fw/request/(?<requestId>\\d+)/chunk/(?<chunk>\\d+)");
  110 + private static final Pattern FW_REQUEST_PATTERN = Pattern.compile(DEVICE_FIRMWARE_REQUEST_TOPIC_PATTERN);
  111 + private static final Pattern SW_REQUEST_PATTERN = Pattern.compile(DEVICE_SOFTWARE_REQUEST_TOPIC_PATTERN);
  112 +
108 113
109 114 private static final String PAYLOAD_TOO_LARGE = "PAYLOAD_TOO_LARGE";
110 115
... ... @@ -314,38 +319,10 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
314 319 } else if (topicName.equals(MqttTopics.DEVICE_CLAIM_TOPIC)) {
315 320 TransportProtos.ClaimDeviceMsg claimDeviceMsg = payloadAdaptor.convertToClaimDevice(deviceSessionCtx, mqttMsg);
316 321 transportService.process(deviceSessionCtx.getSessionInfo(), claimDeviceMsg, getPubAckCallback(ctx, msgId, claimDeviceMsg));
317   - } else if ((fwMatcher = FW_PATTERN.matcher(topicName)).find()) {
318   - String payload = mqttMsg.content().toString(UTF8);
319   - int chunkSize = StringUtils.isNotEmpty(payload) ? Integer.parseInt(payload) : 0;
320   - String requestId = fwMatcher.group("requestId");
321   - int chunk = Integer.parseInt(fwMatcher.group("chunk"));
322   -
323   - if (chunkSize > 0) {
324   - this.fwChunkSizes.put(requestId, chunkSize);
325   - } else {
326   - chunkSize = fwChunkSizes.getOrDefault(requestId, 0);
327   - }
328   -
329   - if (chunkSize > context.getMaxPayloadSize()) {
330   - sendFirmwareError(ctx, PAYLOAD_TOO_LARGE);
331   - return;
332   - }
333   -
334   - String firmwareId = fwSessions.get(requestId);
335   -
336   - if (firmwareId != null) {
337   - sendFirmware(ctx, mqttMsg.variableHeader().packetId(), firmwareId, requestId, chunkSize, chunk);
338   - } else {
339   - TransportProtos.SessionInfoProto sessionInfo = deviceSessionCtx.getSessionInfo();
340   - TransportProtos.GetFirmwareRequestMsg getFirmwareRequestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder()
341   - .setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
342   - .setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
343   - .setTenantIdMSB(sessionInfo.getTenantIdMSB())
344   - .setTenantIdLSB(sessionInfo.getTenantIdLSB())
345   - .build();
346   - transportService.process(deviceSessionCtx.getSessionInfo(), getFirmwareRequestMsg,
347   - new FirmwareCallback(ctx, mqttMsg.variableHeader().packetId(), getFirmwareRequestMsg, requestId, chunkSize, chunk));
348   - }
  322 + } else if ((fwMatcher = FW_REQUEST_PATTERN.matcher(topicName)).find()) {
  323 + getFirmwareCallback(ctx, mqttMsg, msgId, fwMatcher, FirmwareType.FIRMWARE);
  324 + } else if ((fwMatcher = SW_REQUEST_PATTERN.matcher(topicName)).find()) {
  325 + getFirmwareCallback(ctx, mqttMsg, msgId, fwMatcher, FirmwareType.SOFTWARE);
349 326 } else {
350 327 transportService.reportActivity(deviceSessionCtx.getSessionInfo());
351 328 ack(ctx, msgId);
... ... @@ -357,6 +334,41 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
357 334 }
358 335 }
359 336
  337 + private void getFirmwareCallback(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, int msgId, Matcher fwMatcher, FirmwareType type) {
  338 + String payload = mqttMsg.content().toString(UTF8);
  339 + int chunkSize = StringUtils.isNotEmpty(payload) ? Integer.parseInt(payload) : 0;
  340 + String requestId = fwMatcher.group("requestId");
  341 + int chunk = Integer.parseInt(fwMatcher.group("chunk"));
  342 +
  343 + if (chunkSize > 0) {
  344 + this.fwChunkSizes.put(requestId, chunkSize);
  345 + } else {
  346 + chunkSize = fwChunkSizes.getOrDefault(requestId, 0);
  347 + }
  348 +
  349 + if (chunkSize > context.getMaxPayloadSize()) {
  350 + sendFirmwareError(ctx, PAYLOAD_TOO_LARGE);
  351 + return;
  352 + }
  353 +
  354 + String firmwareId = fwSessions.get(requestId);
  355 +
  356 + if (firmwareId != null) {
  357 + sendFirmware(ctx, mqttMsg.variableHeader().packetId(), firmwareId, requestId, chunkSize, chunk, type);
  358 + } else {
  359 + TransportProtos.SessionInfoProto sessionInfo = deviceSessionCtx.getSessionInfo();
  360 + TransportProtos.GetFirmwareRequestMsg getFirmwareRequestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder()
  361 + .setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
  362 + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
  363 + .setTenantIdMSB(sessionInfo.getTenantIdMSB())
  364 + .setTenantIdLSB(sessionInfo.getTenantIdLSB())
  365 + .setType(type.name())
  366 + .build();
  367 + transportService.process(deviceSessionCtx.getSessionInfo(), getFirmwareRequestMsg,
  368 + new FirmwareCallback(ctx, msgId, getFirmwareRequestMsg, requestId, chunkSize, chunk));
  369 + }
  370 + }
  371 +
360 372 private void ack(ChannelHandlerContext ctx, int msgId) {
361 373 if (msgId > 0) {
362 374 ctx.writeAndFlush(createMqttPubAckMsg(msgId));
... ... @@ -435,7 +447,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
435 447 if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) {
436 448 FirmwareId firmwareId = new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB()));
437 449 fwSessions.put(requestId, firmwareId.toString());
438   - sendFirmware(ctx, msgId, firmwareId.toString(), requestId, chunkSize, chunk);
  450 + sendFirmware(ctx, msgId, firmwareId.toString(), requestId, chunkSize, chunk, FirmwareType.valueOf(response.getType()));
439 451 } else {
440 452 sendFirmwareError(ctx, response.getResponseStatus().toString());
441 453 }
... ... @@ -448,13 +460,13 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
448 460 }
449 461 }
450 462
451   - private void sendFirmware(ChannelHandlerContext ctx, int msgId, String firmwareId, String requestId, int chunkSize, int chunk) {
  463 + private void sendFirmware(ChannelHandlerContext ctx, int msgId, String firmwareId, String requestId, int chunkSize, int chunk, FirmwareType type) {
452 464 log.trace("[{}] Send firmware [{}] to device!", sessionId, firmwareId);
453 465 ack(ctx, msgId);
454 466 try {
455 467 byte[] firmwareChunk = context.getFirmwareDataCache().get(firmwareId, chunkSize, chunk);
456 468 deviceSessionCtx.getPayloadAdaptor()
457   - .convertToPublish(deviceSessionCtx, firmwareChunk, requestId, chunk)
  469 + .convertToPublish(deviceSessionCtx, firmwareChunk, requestId, chunk, type)
458 470 .ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
459 471 if (firmwareChunk != null && chunkSize != firmwareChunk.length) {
460 472 scheduler.schedule(() -> processDisconnect(ctx), 60, TimeUnit.SECONDS);
... ... @@ -504,6 +516,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
504 516 case MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC:
505 517 case MqttTopics.DEVICE_FIRMWARE_RESPONSES_TOPIC:
506 518 case MqttTopics.DEVICE_FIRMWARE_ERROR_TOPIC:
  519 + case MqttTopics.DEVICE_SOFTWARE_RESPONSES_TOPIC:
  520 + case MqttTopics.DEVICE_SOFTWARE_ERROR_TOPIC:
507 521 registerSubQoS(topic, grantedQoSList, reqQoS);
508 522 break;
509 523 default:
... ...
... ... @@ -30,6 +30,7 @@ import lombok.extern.slf4j.Slf4j;
30 30 import org.springframework.stereotype.Component;
31 31 import org.springframework.util.StringUtils;
32 32 import org.thingsboard.server.common.data.device.profile.MqttTopics;
  33 +import org.thingsboard.server.common.data.firmware.FirmwareType;
33 34 import org.thingsboard.server.common.transport.adaptor.AdaptorException;
34 35 import org.thingsboard.server.common.transport.adaptor.JsonConverter;
35 36 import org.thingsboard.server.gen.transport.TransportProtos;
... ... @@ -43,6 +44,9 @@ import java.util.Optional;
43 44 import java.util.Set;
44 45 import java.util.UUID;
45 46
  47 +import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT;
  48 +
  49 +
46 50 /**
47 51 * @author Andrew Shvayka
48 52 */
... ... @@ -151,8 +155,8 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor {
151 155 }
152 156
153 157 @Override
154   - public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk) {
155   - return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX + requestId + "/chunk/" + chunk, firmwareChunk));
  158 + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, FirmwareType firmwareType) {
  159 + return Optional.of(createMqttPublishMsg(ctx, String.format(DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT, firmwareType.getKeyPrefix(), requestId, chunk), firmwareChunk));
156 160 }
157 161
158 162 public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException {
... ...
... ... @@ -23,6 +23,7 @@ import io.netty.handler.codec.mqtt.MqttMessage;
23 23 import io.netty.handler.codec.mqtt.MqttMessageType;
24 24 import io.netty.handler.codec.mqtt.MqttPublishMessage;
25 25 import io.netty.handler.codec.mqtt.MqttPublishVariableHeader;
  26 +import org.thingsboard.server.common.data.firmware.FirmwareType;
26 27 import org.thingsboard.server.common.transport.adaptor.AdaptorException;
27 28 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
28 29 import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg;
... ... @@ -77,7 +78,7 @@ public interface MqttTransportAdaptor {
77 78
78 79 Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, ProvisionDeviceResponseMsg provisionResponse) throws AdaptorException;
79 80
80   - Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk) throws AdaptorException;
  81 + Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, FirmwareType firmwareType) throws AdaptorException;
81 82
82 83 default MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, byte[] payloadInBytes) {
83 84 MqttFixedHeader mqttFixedHeader =
... ...
... ... @@ -28,6 +28,7 @@ import lombok.extern.slf4j.Slf4j;
28 28 import org.springframework.stereotype.Component;
29 29 import org.springframework.util.StringUtils;
30 30 import org.thingsboard.server.common.data.device.profile.MqttTopics;
  31 +import org.thingsboard.server.common.data.firmware.FirmwareType;
31 32 import org.thingsboard.server.common.transport.adaptor.AdaptorException;
32 33 import org.thingsboard.server.common.transport.adaptor.JsonConverter;
33 34 import org.thingsboard.server.common.transport.adaptor.ProtoConverter;
... ... @@ -38,6 +39,8 @@ import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionConte
38 39
39 40 import java.util.Optional;
40 41
  42 +import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT;
  43 +
41 44 @Component
42 45 @Slf4j
43 46 public class ProtoMqttAdaptor implements MqttTransportAdaptor {
... ... @@ -165,8 +168,8 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor {
165 168 }
166 169
167 170 @Override
168   - public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk) throws AdaptorException {
169   - return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX + requestId + "/" + chunk, firmwareChunk));
  171 + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, FirmwareType firmwareType) throws AdaptorException {
  172 + return Optional.of(createMqttPublishMsg(ctx, String.format(DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT, firmwareType.getKeyPrefix(), requestId, chunk), firmwareChunk));
170 173 }
171 174
172 175 @Override
... ...
... ... @@ -83,6 +83,8 @@ public interface DeviceDao extends Dao<Device>, TenantEntityDao {
83 83
84 84 PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(UUID tenantId, String type, PageLink pageLink);
85 85
  86 + PageData<Device> findDevicesByTenantIdAndTypeAndEmptySoftware(UUID tenantId, String type, PageLink pageLink);
  87 +
86 88 /**
87 89 * Find device infos by tenantId, type and page link.
88 90 *
... ...
... ... @@ -56,6 +56,7 @@ import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfilePr
56 56 import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
57 57 import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
58 58 import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
  59 +import org.thingsboard.server.common.data.firmware.FirmwareType;
59 60 import org.thingsboard.server.common.data.id.DeviceProfileId;
60 61 import org.thingsboard.server.common.data.id.TenantId;
61 62 import org.thingsboard.server.common.data.page.PageData;
... ... @@ -406,9 +407,31 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
406 407 if (firmware == null) {
407 408 throw new DataValidationException("Can't assign non-existent firmware!");
408 409 }
  410 + if (!firmware.getType().equals(FirmwareType.FIRMWARE)) {
  411 + throw new DataValidationException("Can't assign firmware with type: " + firmware.getType());
  412 + }
409 413 if (firmware.getData() == null) {
410 414 throw new DataValidationException("Can't assign firmware with empty data!");
411 415 }
  416 + if (!firmware.getDeviceProfileId().equals(deviceProfile.getId())) {
  417 + throw new DataValidationException("Can't assign firmware with different deviceProfile!");
  418 + }
  419 + }
  420 +
  421 + if (deviceProfile.getSoftwareId() != null) {
  422 + Firmware software = firmwareService.findFirmwareById(tenantId, deviceProfile.getSoftwareId());
  423 + if (software == null) {
  424 + throw new DataValidationException("Can't assign non-existent software!");
  425 + }
  426 + if (!software.getType().equals(FirmwareType.SOFTWARE)) {
  427 + throw new DataValidationException("Can't assign software with type: " + software.getType());
  428 + }
  429 + if (software.getData() == null) {
  430 + throw new DataValidationException("Can't assign software with empty data!");
  431 + }
  432 + if (!software.getDeviceProfileId().equals(deviceProfile.getId())) {
  433 + throw new DataValidationException("Can't assign firmware with different deviceProfile!");
  434 + }
412 435 }
413 436 }
414 437
... ...
... ... @@ -53,6 +53,7 @@ import org.thingsboard.server.common.data.device.data.Lwm2mDeviceTransportConfig
53 53 import org.thingsboard.server.common.data.device.data.MqttDeviceTransportConfiguration;
54 54 import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
55 55 import org.thingsboard.server.common.data.edge.Edge;
  56 +import org.thingsboard.server.common.data.firmware.FirmwareType;
56 57 import org.thingsboard.server.common.data.id.CustomerId;
57 58 import org.thingsboard.server.common.data.id.DeviceId;
58 59 import org.thingsboard.server.common.data.id.DeviceProfileId;
... ... @@ -361,7 +362,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
361 362
362 363 @Override
363 364 public PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink) {
364   - log.trace("Executing findDevicesByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
  365 + log.trace("Executing findDevicesByTenantIdAndTypeAndEmptyFirmware, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
365 366 validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
366 367 validateString(type, "Incorrect type " + type);
367 368 validatePageLink(pageLink);
... ... @@ -369,6 +370,15 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
369 370 }
370 371
371 372 @Override
  373 + public PageData<Device> findDevicesByTenantIdAndTypeAndEmptySoftware(TenantId tenantId, String type, PageLink pageLink) {
  374 + log.trace("Executing findDevicesByTenantIdAndTypeAndEmptySoftware, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
  375 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  376 + validateString(type, "Incorrect type " + type);
  377 + validatePageLink(pageLink);
  378 + return deviceDao.findDevicesByTenantIdAndTypeAndEmptySoftware(tenantId.getId(), type, pageLink);
  379 + }
  380 +
  381 + @Override
372 382 public PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) {
373 383 log.trace("Executing findDeviceInfosByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
374 384 validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
... ... @@ -696,9 +706,31 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
696 706 if (firmware == null) {
697 707 throw new DataValidationException("Can't assign non-existent firmware!");
698 708 }
  709 + if (!firmware.getType().equals(FirmwareType.FIRMWARE)) {
  710 + throw new DataValidationException("Can't assign firmware with type: " + firmware.getType());
  711 + }
699 712 if (firmware.getData() == null) {
700 713 throw new DataValidationException("Can't assign firmware with empty data!");
701 714 }
  715 + if (!firmware.getDeviceProfileId().equals(device.getDeviceProfileId())) {
  716 + throw new DataValidationException("Can't assign firmware with different deviceProfile!");
  717 + }
  718 + }
  719 +
  720 + if (device.getSoftwareId() != null) {
  721 + Firmware software = firmwareService.findFirmwareById(tenantId, device.getSoftwareId());
  722 + if (software == null) {
  723 + throw new DataValidationException("Can't assign non-existent software!");
  724 + }
  725 + if (!software.getType().equals(FirmwareType.SOFTWARE)) {
  726 + throw new DataValidationException("Can't assign software with type: " + software.getType());
  727 + }
  728 + if (software.getData() == null) {
  729 + throw new DataValidationException("Can't assign software with empty data!");
  730 + }
  731 + if (!software.getDeviceProfileId().equals(device.getDeviceProfileId())) {
  732 + throw new DataValidationException("Can't assign firmware with different deviceProfile!");
  733 + }
702 734 }
703 735 }
704 736 };
... ...
... ... @@ -27,13 +27,17 @@ import org.springframework.cache.CacheManager;
27 27 import org.springframework.cache.annotation.Cacheable;
28 28 import org.springframework.stereotype.Service;
29 29 import org.thingsboard.server.cache.firmware.FirmwareDataCache;
  30 +import org.thingsboard.server.common.data.DeviceProfile;
30 31 import org.thingsboard.server.common.data.Firmware;
31 32 import org.thingsboard.server.common.data.FirmwareInfo;
32 33 import org.thingsboard.server.common.data.Tenant;
  34 +import org.thingsboard.server.common.data.firmware.FirmwareType;
  35 +import org.thingsboard.server.common.data.id.DeviceProfileId;
33 36 import org.thingsboard.server.common.data.id.FirmwareId;
34 37 import org.thingsboard.server.common.data.id.TenantId;
35 38 import org.thingsboard.server.common.data.page.PageData;
36 39 import org.thingsboard.server.common.data.page.PageLink;
  40 +import org.thingsboard.server.dao.device.DeviceProfileDao;
37 41 import org.thingsboard.server.dao.exception.DataValidationException;
38 42 import org.thingsboard.server.dao.service.DataValidator;
39 43 import org.thingsboard.server.dao.service.PaginatedRemover;
... ... @@ -56,6 +60,7 @@ public class BaseFirmwareService implements FirmwareService {
56 60 public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
57 61
58 62 private final TenantDao tenantDao;
  63 + private final DeviceProfileDao deviceProfileDao;
59 64 private final FirmwareDao firmwareDao;
60 65 private final FirmwareInfoDao firmwareInfoDao;
61 66 private final CacheManager cacheManager;
... ... @@ -124,7 +129,8 @@ public class BaseFirmwareService implements FirmwareService {
124 129 public ListenableFuture<FirmwareInfo> findFirmwareInfoByIdAsync(TenantId tenantId, FirmwareId firmwareId) {
125 130 log.trace("Executing findFirmwareInfoByIdAsync [{}]", firmwareId);
126 131 validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId);
127   - return firmwareInfoDao.findByIdAsync(tenantId, firmwareId.getId()); }
  132 + return firmwareInfoDao.findByIdAsync(tenantId, firmwareId.getId());
  133 + }
128 134
129 135 @Override
130 136 public PageData<FirmwareInfo> findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink) {
... ... @@ -135,11 +141,11 @@ public class BaseFirmwareService implements FirmwareService {
135 141 }
136 142
137 143 @Override
138   - public PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink) {
  144 + public PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink) {
139 145 log.trace("Executing findTenantFirmwaresByTenantIdAndHasData, tenantId [{}], hasData [{}] pageLink [{}]", tenantId, hasData, pageLink);
140 146 validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
141 147 validatePageLink(pageLink);
142   - return firmwareInfoDao.findFirmwareInfoByTenantIdAndHasData(tenantId, hasData, pageLink);
  148 + return firmwareInfoDao.findFirmwareInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, firmwareType, hasData, pageLink);
143 149 }
144 150
145 151 @Override
... ... @@ -157,6 +163,10 @@ public class BaseFirmwareService implements FirmwareService {
157 163 throw new DataValidationException("The firmware referenced by the devices cannot be deleted!");
158 164 } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_firmware_device_profile")) {
159 165 throw new DataValidationException("The firmware referenced by the device profile cannot be deleted!");
  166 + } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_software_device")) {
  167 + throw new DataValidationException("The software referenced by the devices cannot be deleted!");
  168 + } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_software_device_profile")) {
  169 + throw new DataValidationException("The software referenced by the device profile cannot be deleted!");
160 170 } else {
161 171 throw t;
162 172 }
... ... @@ -173,29 +183,15 @@ public class BaseFirmwareService implements FirmwareService {
173 183 private DataValidator<FirmwareInfo> firmwareInfoValidator = new DataValidator<>() {
174 184
175 185 @Override
176   - protected void validateDataImpl(TenantId tenantId, FirmwareInfo firmware) {
177   - if (firmware.getTenantId() == null) {
178   - throw new DataValidationException("Firmware should be assigned to tenant!");
179   - } else {
180   - Tenant tenant = tenantDao.findById(firmware.getTenantId(), firmware.getTenantId().getId());
181   - if (tenant == null) {
182   - throw new DataValidationException("Firmware is referencing to non-existent tenant!");
183   - }
184   - }
185   -
186   - if (StringUtils.isEmpty(firmware.getTitle())) {
187   - throw new DataValidationException("Firmware title should be specified!");
188   - }
189   -
190   - if (StringUtils.isEmpty(firmware.getVersion())) {
191   - throw new DataValidationException("Firmware version should be specified!");
192   - }
  186 + protected void validateDataImpl(TenantId tenantId, FirmwareInfo firmwareInfo) {
  187 + validateImpl(firmwareInfo);
193 188 }
194 189
195 190 @Override
196 191 protected void validateUpdate(TenantId tenantId, FirmwareInfo firmware) {
197 192 FirmwareInfo firmwareOld = firmwareInfoDao.findById(tenantId, firmware.getUuidId());
198 193
  194 + validateUpdateDeviceProfile(firmware, firmwareOld);
199 195 BaseFirmwareService.validateUpdate(firmware, firmwareOld);
200 196 }
201 197 };
... ... @@ -204,22 +200,7 @@ public class BaseFirmwareService implements FirmwareService {
204 200
205 201 @Override
206 202 protected void validateDataImpl(TenantId tenantId, Firmware firmware) {
207   - if (firmware.getTenantId() == null) {
208   - throw new DataValidationException("Firmware should be assigned to tenant!");
209   - } else {
210   - Tenant tenant = tenantDao.findById(firmware.getTenantId(), firmware.getTenantId().getId());
211   - if (tenant == null) {
212   - throw new DataValidationException("Firmware is referencing to non-existent tenant!");
213   - }
214   - }
215   -
216   - if (StringUtils.isEmpty(firmware.getTitle())) {
217   - throw new DataValidationException("Firmware title should be specified!");
218   - }
219   -
220   - if (StringUtils.isEmpty(firmware.getVersion())) {
221   - throw new DataValidationException("Firmware version should be specified!");
222   - }
  203 + validateImpl(firmware);
223 204
224 205 if (StringUtils.isEmpty(firmware.getFileName())) {
225 206 throw new DataValidationException("Firmware file name should be specified!");
... ... @@ -267,6 +248,7 @@ public class BaseFirmwareService implements FirmwareService {
267 248 protected void validateUpdate(TenantId tenantId, Firmware firmware) {
268 249 Firmware firmwareOld = firmwareDao.findById(tenantId, firmware.getUuidId());
269 250
  251 + validateUpdateDeviceProfile(firmware, firmwareOld);
270 252 BaseFirmwareService.validateUpdate(firmware, firmwareOld);
271 253
272 254 if (firmwareOld.getData() != null && !firmwareOld.getData().equals(firmware.getData())) {
... ... @@ -275,7 +257,19 @@ public class BaseFirmwareService implements FirmwareService {
275 257 }
276 258 };
277 259
  260 + private void validateUpdateDeviceProfile(FirmwareInfo firmware, FirmwareInfo firmwareOld) {
  261 + if (firmwareOld.getDeviceProfileId() != null && !firmwareOld.getDeviceProfileId().equals(firmware.getDeviceProfileId())) {
  262 + if (firmwareInfoDao.isFirmwareUsed(firmwareOld.getId(), firmware.getType(), firmwareOld.getDeviceProfileId())) {
  263 + throw new DataValidationException("Can`t update deviceProfileId because firmware is already in use!");
  264 + }
  265 + }
  266 + }
  267 +
278 268 private static void validateUpdate(FirmwareInfo firmware, FirmwareInfo firmwareOld) {
  269 + if (!firmwareOld.getType().equals(firmware.getType())) {
  270 + throw new DataValidationException("Updating type is prohibited!");
  271 + }
  272 +
279 273 if (!firmwareOld.getTitle().equals(firmware.getTitle())) {
280 274 throw new DataValidationException("Updating firmware title is prohibited!");
281 275 }
... ... @@ -305,6 +299,36 @@ public class BaseFirmwareService implements FirmwareService {
305 299 }
306 300 }
307 301
  302 + private void validateImpl(FirmwareInfo firmwareInfo) {
  303 + if (firmwareInfo.getTenantId() == null) {
  304 + throw new DataValidationException("Firmware should be assigned to tenant!");
  305 + } else {
  306 + Tenant tenant = tenantDao.findById(firmwareInfo.getTenantId(), firmwareInfo.getTenantId().getId());
  307 + if (tenant == null) {
  308 + throw new DataValidationException("Firmware is referencing to non-existent tenant!");
  309 + }
  310 + }
  311 +
  312 + if (firmwareInfo.getDeviceProfileId() != null) {
  313 + DeviceProfile deviceProfile = deviceProfileDao.findById(firmwareInfo.getTenantId(), firmwareInfo.getDeviceProfileId().getId());
  314 + if (deviceProfile == null) {
  315 + throw new DataValidationException("Firmware is referencing to non-existent device profile!");
  316 + }
  317 + }
  318 +
  319 + if (firmwareInfo.getType() == null) {
  320 + throw new DataValidationException("Type should be specified!");
  321 + }
  322 +
  323 + if (StringUtils.isEmpty(firmwareInfo.getTitle())) {
  324 + throw new DataValidationException("Firmware title should be specified!");
  325 + }
  326 +
  327 + if (StringUtils.isEmpty(firmwareInfo.getVersion())) {
  328 + throw new DataValidationException("Firmware version should be specified!");
  329 + }
  330 + }
  331 +
308 332 private PaginatedRemover<TenantId, FirmwareInfo> tenantFirmwareRemover =
309 333 new PaginatedRemover<>() {
310 334
... ...
... ... @@ -16,6 +16,9 @@
16 16 package org.thingsboard.server.dao.firmware;
17 17
18 18 import org.thingsboard.server.common.data.FirmwareInfo;
  19 +import org.thingsboard.server.common.data.firmware.FirmwareType;
  20 +import org.thingsboard.server.common.data.id.DeviceProfileId;
  21 +import org.thingsboard.server.common.data.id.FirmwareId;
19 22 import org.thingsboard.server.common.data.id.TenantId;
20 23 import org.thingsboard.server.common.data.page.PageData;
21 24 import org.thingsboard.server.common.data.page.PageLink;
... ... @@ -27,6 +30,8 @@ public interface FirmwareInfoDao extends Dao<FirmwareInfo> {
27 30
28 31 PageData<FirmwareInfo> findFirmwareInfoByTenantId(TenantId tenantId, PageLink pageLink);
29 32
30   - PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink);
  33 + PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink);
  34 +
  35 + boolean isFirmwareUsed(FirmwareId firmwareId, FirmwareType type, DeviceProfileId deviceProfileId);
31 36
32 37 }
... ...
... ... @@ -154,6 +154,7 @@ public class ModelConstants {
154 154 public static final String DEVICE_DEVICE_PROFILE_ID_PROPERTY = "device_profile_id";
155 155 public static final String DEVICE_DEVICE_DATA_PROPERTY = "device_data";
156 156 public static final String DEVICE_FIRMWARE_ID_PROPERTY = "firmware_id";
  157 + public static final String DEVICE_SOFTWARE_ID_PROPERTY = "software_id";
157 158
158 159 public static final String DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_and_search_text";
159 160 public static final String DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_by_type_and_search_text";
... ... @@ -178,6 +179,7 @@ public class ModelConstants {
178 179 public static final String DEVICE_PROFILE_DEFAULT_QUEUE_NAME_PROPERTY = "default_queue_name";
179 180 public static final String DEVICE_PROFILE_PROVISION_DEVICE_KEY = "provision_device_key";
180 181 public static final String DEVICE_PROFILE_FIRMWARE_ID_PROPERTY = "firmware_id";
  182 + public static final String DEVICE_PROFILE_SOFTWARE_ID_PROPERTY = "software_id";
181 183
182 184 /**
183 185 * Cassandra entityView constants.
... ... @@ -476,6 +478,8 @@ public class ModelConstants {
476 478 */
477 479 public static final String FIRMWARE_TABLE_NAME = "firmware";
478 480 public static final String FIRMWARE_TENANT_ID_COLUMN = TENANT_ID_COLUMN;
  481 + public static final String FIRMWARE_DEVICE_PROFILE_ID_COLUMN = "device_profile_id";
  482 + public static final String FIRMWARE_TYPE_COLUMN = "type";
479 483 public static final String FIRMWARE_TITLE_COLUMN = TITLE_PROPERTY;
480 484 public static final String FIRMWARE_VERSION_COLUMN = "version";
481 485 public static final String FIRMWARE_FILE_NAME_COLUMN = "file_name";
... ...
... ... @@ -22,6 +22,7 @@ import lombok.EqualsAndHashCode;
22 22 import org.hibernate.annotations.Type;
23 23 import org.hibernate.annotations.TypeDef;
24 24 import org.hibernate.annotations.TypeDefs;
  25 +import org.thingsboard.common.util.JacksonUtil;
25 26 import org.thingsboard.server.common.data.Device;
26 27 import org.thingsboard.server.common.data.device.data.DeviceData;
27 28 import org.thingsboard.server.common.data.id.CustomerId;
... ... @@ -32,7 +33,6 @@ import org.thingsboard.server.common.data.id.TenantId;
32 33 import org.thingsboard.server.dao.model.BaseSqlEntity;
33 34 import org.thingsboard.server.dao.model.ModelConstants;
34 35 import org.thingsboard.server.dao.model.SearchTextEntity;
35   -import org.thingsboard.common.util.JacksonUtil;
36 36 import org.thingsboard.server.dao.util.mapping.JsonBinaryType;
37 37 import org.thingsboard.server.dao.util.mapping.JsonStringType;
38 38
... ... @@ -43,8 +43,8 @@ import java.util.UUID;
43 43 @Data
44 44 @EqualsAndHashCode(callSuper = true)
45 45 @TypeDefs({
46   - @TypeDef(name = "json", typeClass = JsonStringType.class),
47   - @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
  46 + @TypeDef(name = "json", typeClass = JsonStringType.class),
  47 + @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
48 48 })
49 49 @MappedSuperclass
50 50 public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEntity<T> implements SearchTextEntity<T> {
... ... @@ -77,6 +77,9 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
77 77 @Column(name = ModelConstants.DEVICE_FIRMWARE_ID_PROPERTY, columnDefinition = "uuid")
78 78 private UUID firmwareId;
79 79
  80 + @Column(name = ModelConstants.DEVICE_SOFTWARE_ID_PROPERTY, columnDefinition = "uuid")
  81 + private UUID softwareId;
  82 +
80 83 @Type(type = "jsonb")
81 84 @Column(name = ModelConstants.DEVICE_DEVICE_DATA_PROPERTY, columnDefinition = "jsonb")
82 85 private JsonNode deviceData;
... ... @@ -102,6 +105,9 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
102 105 if (device.getFirmwareId() != null) {
103 106 this.firmwareId = device.getFirmwareId().getId();
104 107 }
  108 + if (device.getSoftwareId() != null) {
  109 + this.softwareId = device.getSoftwareId().getId();
  110 + }
105 111 this.deviceData = JacksonUtil.convertValue(device.getDeviceData(), ObjectNode.class);
106 112 this.name = device.getName();
107 113 this.type = device.getType();
... ... @@ -122,6 +128,7 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
122 128 this.searchText = deviceEntity.getSearchText();
123 129 this.additionalInfo = deviceEntity.getAdditionalInfo();
124 130 this.firmwareId = deviceEntity.getFirmwareId();
  131 + this.softwareId = deviceEntity.getSoftwareId();
125 132 }
126 133
127 134 @Override
... ... @@ -149,6 +156,9 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
149 156 if (firmwareId != null) {
150 157 device.setFirmwareId(new FirmwareId(firmwareId));
151 158 }
  159 + if (softwareId != null) {
  160 + device.setSoftwareId(new FirmwareId(softwareId));
  161 + }
152 162 device.setDeviceData(JacksonUtil.convertValue(deviceData, DeviceData.class));
153 163 device.setName(name);
154 164 device.setType(type);
... ...
... ... @@ -21,9 +21,10 @@ import lombok.Data;
21 21 import lombok.EqualsAndHashCode;
22 22 import org.hibernate.annotations.Type;
23 23 import org.hibernate.annotations.TypeDef;
  24 +import org.thingsboard.common.util.JacksonUtil;
24 25 import org.thingsboard.server.common.data.DeviceProfile;
25   -import org.thingsboard.server.common.data.DeviceProfileType;
26 26 import org.thingsboard.server.common.data.DeviceProfileProvisionType;
  27 +import org.thingsboard.server.common.data.DeviceProfileType;
27 28 import org.thingsboard.server.common.data.DeviceTransportType;
28 29 import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
29 30 import org.thingsboard.server.common.data.id.DeviceProfileId;
... ... @@ -33,7 +34,6 @@ import org.thingsboard.server.common.data.id.TenantId;
33 34 import org.thingsboard.server.dao.model.BaseSqlEntity;
34 35 import org.thingsboard.server.dao.model.ModelConstants;
35 36 import org.thingsboard.server.dao.model.SearchTextEntity;
36   -import org.thingsboard.common.util.JacksonUtil;
37 37 import org.thingsboard.server.dao.util.mapping.JsonBinaryType;
38 38
39 39 import javax.persistence.Column;
... ... @@ -87,12 +87,15 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
87 87 @Column(name = ModelConstants.DEVICE_PROFILE_PROFILE_DATA_PROPERTY, columnDefinition = "jsonb")
88 88 private JsonNode profileData;
89 89
90   - @Column(name=ModelConstants.DEVICE_PROFILE_PROVISION_DEVICE_KEY)
  90 + @Column(name = ModelConstants.DEVICE_PROFILE_PROVISION_DEVICE_KEY)
91 91 private String provisionDeviceKey;
92 92
93   - @Column(name=ModelConstants.DEVICE_PROFILE_FIRMWARE_ID_PROPERTY)
  93 + @Column(name = ModelConstants.DEVICE_PROFILE_FIRMWARE_ID_PROPERTY)
94 94 private UUID firmwareId;
95 95
  96 + @Column(name = ModelConstants.DEVICE_PROFILE_SOFTWARE_ID_PROPERTY)
  97 + private UUID softwareId;
  98 +
96 99 public DeviceProfileEntity() {
97 100 super();
98 101 }
... ... @@ -120,6 +123,9 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
120 123 if (deviceProfile.getFirmwareId() != null) {
121 124 this.firmwareId = deviceProfile.getFirmwareId().getId();
122 125 }
  126 + if (deviceProfile.getSoftwareId() != null) {
  127 + this.firmwareId = deviceProfile.getSoftwareId().getId();
  128 + }
123 129 }
124 130
125 131 @Override
... ... @@ -160,6 +166,10 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
160 166 deviceProfile.setFirmwareId(new FirmwareId(firmwareId));
161 167 }
162 168
  169 + if (softwareId != null) {
  170 + deviceProfile.setSoftwareId(new FirmwareId(softwareId));
  171 + }
  172 +
163 173 return deviceProfile;
164 174 }
165 175 }
... ...
... ... @@ -21,6 +21,8 @@ import lombok.EqualsAndHashCode;
21 21 import org.hibernate.annotations.Type;
22 22 import org.hibernate.annotations.TypeDef;
23 23 import org.thingsboard.server.common.data.Firmware;
  24 +import org.thingsboard.server.common.data.firmware.FirmwareType;
  25 +import org.thingsboard.server.common.data.id.DeviceProfileId;
24 26 import org.thingsboard.server.common.data.id.FirmwareId;
25 27 import org.thingsboard.server.common.data.id.TenantId;
26 28 import org.thingsboard.server.dao.model.BaseSqlEntity;
... ... @@ -30,6 +32,8 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType;
30 32
31 33 import javax.persistence.Column;
32 34 import javax.persistence.Entity;
  35 +import javax.persistence.EnumType;
  36 +import javax.persistence.Enumerated;
33 37 import javax.persistence.Lob;
34 38 import javax.persistence.Table;
35 39 import java.nio.ByteBuffer;
... ... @@ -40,10 +44,12 @@ import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_
40 44 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN;
41 45 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_COLUMN;
42 46 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_SIZE_COLUMN;
  47 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DEVICE_PROFILE_ID_COLUMN;
43 48 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN;
44 49 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME;
45 50 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN;
46 51 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN;
  52 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TYPE_COLUMN;
47 53 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN;
48 54 import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY;
49 55
... ... @@ -57,6 +63,13 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex
57 63 @Column(name = FIRMWARE_TENANT_ID_COLUMN)
58 64 private UUID tenantId;
59 65
  66 + @Column(name = FIRMWARE_DEVICE_PROFILE_ID_COLUMN)
  67 + private UUID deviceProfileId;
  68 +
  69 + @Enumerated(EnumType.STRING)
  70 + @Column(name = FIRMWARE_TYPE_COLUMN)
  71 + private FirmwareType type;
  72 +
60 73 @Column(name = FIRMWARE_TITLE_COLUMN)
61 74 private String title;
62 75
... ... @@ -97,6 +110,10 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex
97 110 this.createdTime = firmware.getCreatedTime();
98 111 this.setUuid(firmware.getUuidId());
99 112 this.tenantId = firmware.getTenantId().getId();
  113 + if (firmware.getDeviceProfileId() != null) {
  114 + this.deviceProfileId = firmware.getDeviceProfileId().getId();
  115 + }
  116 + this.type = firmware.getType();
100 117 this.title = firmware.getTitle();
101 118 this.version = firmware.getVersion();
102 119 this.fileName = firmware.getFileName();
... ... @@ -123,6 +140,10 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex
123 140 Firmware firmware = new Firmware(new FirmwareId(id));
124 141 firmware.setCreatedTime(createdTime);
125 142 firmware.setTenantId(new TenantId(tenantId));
  143 + if (deviceProfileId != null) {
  144 + firmware.setDeviceProfileId(new DeviceProfileId(deviceProfileId));
  145 + }
  146 + firmware.setType(type);
126 147 firmware.setTitle(title);
127 148 firmware.setVersion(version);
128 149 firmware.setFileName(fileName);
... ...
... ... @@ -22,6 +22,8 @@ import org.hibernate.annotations.Type;
22 22 import org.hibernate.annotations.TypeDef;
23 23 import org.thingsboard.common.util.JacksonUtil;
24 24 import org.thingsboard.server.common.data.FirmwareInfo;
  25 +import org.thingsboard.server.common.data.firmware.FirmwareType;
  26 +import org.thingsboard.server.common.data.id.DeviceProfileId;
25 27 import org.thingsboard.server.common.data.id.FirmwareId;
26 28 import org.thingsboard.server.common.data.id.TenantId;
27 29 import org.thingsboard.server.dao.model.BaseSqlEntity;
... ... @@ -31,6 +33,8 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType;
31 33
32 34 import javax.persistence.Column;
33 35 import javax.persistence.Entity;
  36 +import javax.persistence.EnumType;
  37 +import javax.persistence.Enumerated;
34 38 import javax.persistence.Table;
35 39 import javax.persistence.Transient;
36 40 import java.util.UUID;
... ... @@ -38,13 +42,13 @@ import java.util.UUID;
38 42 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_ALGORITHM_COLUMN;
39 43 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_COLUMN;
40 44 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN;
41   -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_COLUMN;
42 45 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_SIZE_COLUMN;
  46 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DEVICE_PROFILE_ID_COLUMN;
43 47 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN;
44   -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_HAS_DATA_PROPERTY;
45 48 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME;
46 49 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN;
47 50 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN;
  51 +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TYPE_COLUMN;
48 52 import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN;
49 53 import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY;
50 54
... ... @@ -58,6 +62,13 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S
58 62 @Column(name = FIRMWARE_TENANT_ID_COLUMN)
59 63 private UUID tenantId;
60 64
  65 + @Column(name = FIRMWARE_DEVICE_PROFILE_ID_COLUMN)
  66 + private UUID deviceProfileId;
  67 +
  68 + @Enumerated(EnumType.STRING)
  69 + @Column(name = FIRMWARE_TYPE_COLUMN)
  70 + private FirmwareType type;
  71 +
61 72 @Column(name = FIRMWARE_TITLE_COLUMN)
62 73 private String title;
63 74
... ... @@ -97,6 +108,10 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S
97 108 this.createdTime = firmware.getCreatedTime();
98 109 this.setUuid(firmware.getUuidId());
99 110 this.tenantId = firmware.getTenantId().getId();
  111 + this.type = firmware.getType();
  112 + if (firmware.getDeviceProfileId() != null) {
  113 + this.deviceProfileId = firmware.getDeviceProfileId().getId();
  114 + }
100 115 this.title = firmware.getTitle();
101 116 this.version = firmware.getVersion();
102 117 this.fileName = firmware.getFileName();
... ... @@ -107,12 +122,14 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S
107 122 this.additionalInfo = firmware.getAdditionalInfo();
108 123 }
109 124
110   - public FirmwareInfoEntity(UUID id, long createdTime, UUID tenantId, String title, String version,
  125 + public FirmwareInfoEntity(UUID id, long createdTime, UUID tenantId, UUID deviceProfileId, FirmwareType type, String title, String version,
111 126 String fileName, String contentType, String checksumAlgorithm, String checksum, Long dataSize,
112 127 Object additionalInfo, boolean hasData) {
113 128 this.id = id;
114 129 this.createdTime = createdTime;
115 130 this.tenantId = tenantId;
  131 + this.deviceProfileId = deviceProfileId;
  132 + this.type = type;
116 133 this.title = title;
117 134 this.version = version;
118 135 this.fileName = fileName;
... ... @@ -139,6 +156,10 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S
139 156 FirmwareInfo firmware = new FirmwareInfo(new FirmwareId(id));
140 157 firmware.setCreatedTime(createdTime);
141 158 firmware.setTenantId(new TenantId(tenantId));
  159 + if (deviceProfileId != null) {
  160 + firmware.setDeviceProfileId(new DeviceProfileId(deviceProfileId));
  161 + }
  162 + firmware.setType(type);
142 163 firmware.setTitle(title);
143 164 firmware.setVersion(version);
144 165 firmware.setFileName(fileName);
... ...
... ... @@ -104,6 +104,15 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit
104 104 @Param("textSearch") String textSearch,
105 105 Pageable pageable);
106 106
  107 + @Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId " +
  108 + "AND d.type = :type " +
  109 + "AND d.softwareId = null " +
  110 + "AND LOWER(d.searchText) LIKE LOWER(CONCAT(:textSearch, '%'))")
  111 + Page<DeviceEntity> findByTenantIdAndTypeAndSoftwareIdIsNull(@Param("tenantId") UUID tenantId,
  112 + @Param("type") String type,
  113 + @Param("textSearch") String textSearch,
  114 + Pageable pageable);
  115 +
107 116 @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " +
108 117 "FROM DeviceEntity d " +
109 118 "LEFT JOIN CustomerEntity c on c.id = d.customerId " +
... ...
... ... @@ -165,6 +165,16 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device>
165 165 }
166 166
167 167 @Override
  168 + public PageData<Device> findDevicesByTenantIdAndTypeAndEmptySoftware(UUID tenantId, String type, PageLink pageLink) {
  169 + return DaoUtil.toPageData(
  170 + deviceRepository.findByTenantIdAndTypeAndSoftwareIdIsNull(
  171 + tenantId,
  172 + type,
  173 + Objects.toString(pageLink.getTextSearch(), ""),
  174 + DaoUtil.toPageable(pageLink)));
  175 + }
  176 +
  177 + @Override
168 178 public PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(UUID tenantId, String type, PageLink pageLink) {
169 179 return DaoUtil.toPageData(
170 180 deviceRepository.findDeviceInfosByTenantIdAndType(
... ...
... ... @@ -20,27 +20,41 @@ import org.springframework.data.domain.Pageable;
20 20 import org.springframework.data.jpa.repository.Query;
21 21 import org.springframework.data.repository.CrudRepository;
22 22 import org.springframework.data.repository.query.Param;
  23 +import org.thingsboard.server.common.data.firmware.FirmwareType;
23 24 import org.thingsboard.server.dao.model.sql.FirmwareInfoEntity;
24 25
25 26 import java.util.UUID;
26 27
27 28 public interface FirmwareInfoRepository extends CrudRepository<FirmwareInfoEntity, UUID> {
28   - @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " +
  29 + @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " +
29 30 "f.tenantId = :tenantId " +
30 31 "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
31 32 Page<FirmwareInfoEntity> findAllByTenantId(@Param("tenantId") UUID tenantId,
32 33 @Param("searchText") String searchText,
33 34 Pageable pageable);
34 35
35   - @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " +
  36 + @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " +
36 37 "f.tenantId = :tenantId " +
  38 + "AND f.deviceProfileId = :deviceProfileId " +
  39 + "AND f.type = :type " +
37 40 "AND ((f.data IS NOT NULL AND :hasData = true) OR (f.data IS NULL AND :hasData = false ))" +
38 41 "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
39   - Page<FirmwareInfoEntity> findAllByTenantIdAndHasData(@Param("tenantId") UUID tenantId,
40   - @Param("hasData") boolean hasData,
41   - @Param("searchText") String searchText,
42   - Pageable pageable);
  42 + Page<FirmwareInfoEntity> findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData(@Param("tenantId") UUID tenantId,
  43 + @Param("deviceProfileId") UUID deviceProfileId,
  44 + @Param("type") FirmwareType type,
  45 + @Param("hasData") boolean hasData,
  46 + @Param("searchText") String searchText,
  47 + Pageable pageable);
43 48
44   - @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE f.id = :id")
  49 + @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE f.id = :id")
45 50 FirmwareInfoEntity findFirmwareInfoById(@Param("id") UUID id);
  51 +
  52 + @Query(value = "SELECT exists(SELECT * " +
  53 + "FROM device_profile AS dp " +
  54 + "LEFT JOIN device AS d ON dp.id = d.device_profile_id " +
  55 + "WHERE dp.id = :deviceProfileId AND " +
  56 + "(('FIRMWARE' = :type AND (dp.firmware_id = :firmwareId OR d.firmware_id = :firmwareId)) " +
  57 + "OR ('SOFTWARE' = :type AND (dp.software_id = :firmwareId or d.software_id = :firmwareId))))", nativeQuery = true)
  58 + boolean isFirmwareUsed(@Param("firmwareId") UUID firmwareId, @Param("deviceProfileId") UUID deviceProfileId, @Param("type") String type);
  59 +
46 60 }
... ...
... ... @@ -20,6 +20,9 @@ import org.springframework.beans.factory.annotation.Autowired;
20 20 import org.springframework.data.repository.CrudRepository;
21 21 import org.springframework.stereotype.Component;
22 22 import org.thingsboard.server.common.data.FirmwareInfo;
  23 +import org.thingsboard.server.common.data.firmware.FirmwareType;
  24 +import org.thingsboard.server.common.data.id.DeviceProfileId;
  25 +import org.thingsboard.server.common.data.id.FirmwareId;
23 26 import org.thingsboard.server.common.data.id.TenantId;
24 27 import org.thingsboard.server.common.data.page.PageData;
25 28 import org.thingsboard.server.common.data.page.PageLink;
... ... @@ -73,12 +76,19 @@ public class JpaFirmwareInfoDao extends JpaAbstractSearchTextDao<FirmwareInfoEnt
73 76 }
74 77
75 78 @Override
76   - public PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink) {
  79 + public PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink) {
77 80 return DaoUtil.toPageData(firmwareInfoRepository
78   - .findAllByTenantIdAndHasData(
  81 + .findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData(
79 82 tenantId.getId(),
  83 + deviceProfileId.getId(),
  84 + firmwareType,
80 85 hasData,
81 86 Objects.toString(pageLink.getTextSearch(), ""),
82 87 DaoUtil.toPageable(pageLink)));
83 88 }
  89 +
  90 + @Override
  91 + public boolean isFirmwareUsed(FirmwareId firmwareId, FirmwareType type, DeviceProfileId deviceProfileId) {
  92 + return firmwareInfoRepository.isFirmwareUsed(firmwareId.getId(), deviceProfileId.getId(), type.name());
  93 + }
84 94 }
... ...
... ... @@ -162,6 +162,8 @@ CREATE TABLE IF NOT EXISTS firmware (
162 162 id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY,
163 163 created_time bigint NOT NULL,
164 164 tenant_id uuid NOT NULL,
  165 + device_profile_id uuid ,
  166 + type varchar(32) NOT NULL,
165 167 title varchar(255) NOT NULL,
166 168 version varchar(255) NOT NULL,
167 169 file_name varchar(255),
... ... @@ -188,13 +190,15 @@ CREATE TABLE IF NOT EXISTS device_profile (
188 190 is_default boolean,
189 191 tenant_id uuid,
190 192 firmware_id uuid,
  193 + software_id uuid,
191 194 default_rule_chain_id uuid,
192 195 default_queue_name varchar(255),
193 196 provision_device_key varchar,
194 197 CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name),
195 198 CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key),
196 199 CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id),
197   - CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id)
  200 + CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id),
  201 + CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES firmware(id)
198 202 );
199 203
200 204 CREATE TABLE IF NOT EXISTS device (
... ... @@ -210,9 +214,11 @@ CREATE TABLE IF NOT EXISTS device (
210 214 search_text varchar(255),
211 215 tenant_id uuid,
212 216 firmware_id uuid,
  217 + software_id uuid,
213 218 CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name),
214 219 CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id),
215   - CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id)
  220 + CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id),
  221 + CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES firmware(id)
216 222 );
217 223
218 224 CREATE TABLE IF NOT EXISTS device_credentials (
... ...
... ... @@ -45,3 +45,4 @@ CREATE INDEX IF NOT EXISTS idx_asset_type ON asset(tenant_id, type);
45 45 CREATE INDEX IF NOT EXISTS idx_attribute_kv_by_key_and_last_update_ts ON attribute_kv(entity_id, attribute_key, last_update_ts desc);
46 46
47 47 CREATE INDEX IF NOT EXISTS idx_audit_log_tenant_id_and_created_time ON audit_log(tenant_id, created_time);
  48 +
... ...
... ... @@ -180,6 +180,8 @@ CREATE TABLE IF NOT EXISTS firmware (
180 180 id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY,
181 181 created_time bigint NOT NULL,
182 182 tenant_id uuid NOT NULL,
  183 + device_profile_id uuid ,
  184 + type varchar(32) NOT NULL,
183 185 title varchar(255) NOT NULL,
184 186 version varchar(255) NOT NULL,
185 187 file_name varchar(255),
... ... @@ -191,6 +193,7 @@ CREATE TABLE IF NOT EXISTS firmware (
191 193 additional_info varchar,
192 194 search_text varchar(255),
193 195 CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version)
  196 +-- CONSTRAINT fk_device_profile_firmware FOREIGN KEY (device_profile_id) REFERENCES device_profile(id) ON DELETE CASCADE
194 197 );
195 198
196 199 CREATE TABLE IF NOT EXISTS device_profile (
... ... @@ -206,15 +209,26 @@ CREATE TABLE IF NOT EXISTS device_profile (
206 209 is_default boolean,
207 210 tenant_id uuid,
208 211 firmware_id uuid,
  212 + software_id uuid,
209 213 default_rule_chain_id uuid,
210 214 default_queue_name varchar(255),
211 215 provision_device_key varchar,
212 216 CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name),
213 217 CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key),
214 218 CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id),
215   - CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id)
  219 + CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id),
  220 + CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES firmware(id)
216 221 );
217 222
  223 +-- We will use one-to-many relation in the first release and extend this feature in case of user requests
  224 +-- CREATE TABLE IF NOT EXISTS device_profile_firmware (
  225 +-- device_profile_id uuid NOT NULL,
  226 +-- firmware_id uuid NOT NULL,
  227 +-- CONSTRAINT device_profile_firmware_unq_key UNIQUE (device_profile_id, firmware_id),
  228 +-- CONSTRAINT fk_device_profile_firmware_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id) ON DELETE CASCADE,
  229 +-- CONSTRAINT fk_device_profile_firmware_firmware FOREIGN KEY (firmware_id) REFERENCES firmware(id) ON DELETE CASCADE,
  230 +-- );
  231 +
218 232 CREATE TABLE IF NOT EXISTS device (
219 233 id uuid NOT NULL CONSTRAINT device_pkey PRIMARY KEY,
220 234 created_time bigint NOT NULL,
... ... @@ -228,9 +242,11 @@ CREATE TABLE IF NOT EXISTS device (
228 242 search_text varchar(255),
229 243 tenant_id uuid,
230 244 firmware_id uuid,
  245 + software_id uuid,
231 246 CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name),
232 247 CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id),
233   - CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id)
  248 + CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id),
  249 + CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES firmware(id)
234 250 );
235 251
236 252 CREATE TABLE IF NOT EXISTS device_credentials (
... ...
... ... @@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.DeviceProfileInfo;
30 30 import org.thingsboard.server.common.data.DeviceTransportType;
31 31 import org.thingsboard.server.common.data.Firmware;
32 32 import org.thingsboard.server.common.data.Tenant;
  33 +import org.thingsboard.server.common.data.firmware.FirmwareType;
33 34 import org.thingsboard.server.common.data.id.TenantId;
34 35 import org.thingsboard.server.common.data.page.PageData;
35 36 import org.thingsboard.server.common.data.page.PageLink;
... ... @@ -43,6 +44,8 @@ import java.util.concurrent.ExecutionException;
43 44 import java.util.concurrent.Executors;
44 45 import java.util.stream.Collectors;
45 46
  47 +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
  48 +
46 49 public class BaseDeviceProfileServiceTest extends AbstractServiceTest {
47 50
48 51 private IdComparator<DeviceProfile> idComparator = new IdComparator<>();
... ... @@ -97,6 +100,8 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest {
97 100
98 101 Firmware firmware = new Firmware();
99 102 firmware.setTenantId(tenantId);
  103 + firmware.setDeviceProfileId(savedDeviceProfile.getId());
  104 + firmware.setType(FIRMWARE);
100 105 firmware.setTitle("my firmware");
101 106 firmware.setVersion("v1.0");
102 107 firmware.setFileName("test.txt");
... ...
... ... @@ -20,10 +20,13 @@ import org.apache.commons.lang3.RandomStringUtils;
20 20 import org.junit.After;
21 21 import org.junit.Assert;
22 22 import org.junit.Before;
  23 +import org.junit.Rule;
23 24 import org.junit.Test;
  25 +import org.junit.rules.ExpectedException;
24 26 import org.thingsboard.server.common.data.Customer;
25 27 import org.thingsboard.server.common.data.Device;
26 28 import org.thingsboard.server.common.data.DeviceInfo;
  29 +import org.thingsboard.server.common.data.DeviceProfile;
27 30 import org.thingsboard.server.common.data.EntitySubtype;
28 31 import org.thingsboard.server.common.data.Firmware;
29 32 import org.thingsboard.server.common.data.Tenant;
... ... @@ -42,6 +45,7 @@ import java.util.ArrayList;
42 45 import java.util.Collections;
43 46 import java.util.List;
44 47
  48 +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
45 49 import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
46 50
47 51 public abstract class BaseDeviceServiceTest extends AbstractServiceTest {
... ... @@ -66,6 +70,9 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest {
66 70 tenantProfileService.deleteTenantProfiles(anotherTenantId);
67 71 }
68 72
  73 + @Rule
  74 + public ExpectedException thrown = ExpectedException.none();
  75 +
69 76 @Test
70 77 public void testSaveDevicesWithoutMaxDeviceLimit() {
71 78 Device device = this.saveDevice(tenantId, "My device");
... ... @@ -183,6 +190,8 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest {
183 190
184 191 Firmware firmware = new Firmware();
185 192 firmware.setTenantId(tenantId);
  193 + firmware.setDeviceProfileId(device.getDeviceProfileId());
  194 + firmware.setType(FIRMWARE);
186 195 firmware.setTitle("my firmware");
187 196 firmware.setVersion("v1.0");
188 197 firmware.setFileName("test.txt");
... ... @@ -198,6 +207,40 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest {
198 207 Device foundDevice = deviceService.findDeviceById(tenantId, savedDevice.getId());
199 208 Assert.assertEquals(foundDevice.getName(), savedDevice.getName());
200 209 }
  210 +
  211 + @Test
  212 + public void testAssignFirmwareToDeviceWithDifferentDeviceProfile() {
  213 + Device device = new Device();
  214 + device.setTenantId(tenantId);
  215 + device.setName("My device");
  216 + device.setType("default");
  217 + Device savedDevice = deviceService.saveDevice(device);
  218 +
  219 + Assert.assertNotNull(savedDevice);
  220 +
  221 + DeviceProfile deviceProfile = createDeviceProfile(tenantId, "New device Profile");
  222 + DeviceProfile savedProfile = deviceProfileService.saveDeviceProfile(deviceProfile);
  223 + Assert.assertNotNull(savedProfile);
  224 +
  225 + Firmware firmware = new Firmware();
  226 + firmware.setTenantId(tenantId);
  227 + firmware.setDeviceProfileId(savedProfile.getId());
  228 + firmware.setType(FIRMWARE);
  229 + firmware.setTitle("my firmware");
  230 + firmware.setVersion("v1.0");
  231 + firmware.setFileName("test.txt");
  232 + firmware.setContentType("text/plain");
  233 + firmware.setChecksumAlgorithm("sha256");
  234 + firmware.setChecksum("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a");
  235 + firmware.setData(ByteBuffer.wrap(new byte[]{1}));
  236 + Firmware savedFirmware = firmwareService.saveFirmware(firmware);
  237 +
  238 + savedDevice.setFirmwareId(savedFirmware.getId());
  239 +
  240 + thrown.expect(DataValidationException.class);
  241 + thrown.expectMessage("Can't assign firmware with different deviceProfile!");
  242 + deviceService.saveDevice(savedDevice);
  243 + }
201 244
202 245 @Test(expected = DataValidationException.class)
203 246 public void testSaveDeviceWithEmptyName() {
... ...
... ... @@ -19,13 +19,16 @@ import com.datastax.oss.driver.api.core.uuid.Uuids;
19 19 import org.junit.After;
20 20 import org.junit.Assert;
21 21 import org.junit.Before;
  22 +import org.junit.Rule;
22 23 import org.junit.Test;
  24 +import org.junit.rules.ExpectedException;
23 25 import org.thingsboard.common.util.JacksonUtil;
24 26 import org.thingsboard.server.common.data.Device;
25 27 import org.thingsboard.server.common.data.DeviceProfile;
26 28 import org.thingsboard.server.common.data.Firmware;
27 29 import org.thingsboard.server.common.data.FirmwareInfo;
28 30 import org.thingsboard.server.common.data.Tenant;
  31 +import org.thingsboard.server.common.data.id.DeviceProfileId;
29 32 import org.thingsboard.server.common.data.id.TenantId;
30 33 import org.thingsboard.server.common.data.page.PageData;
31 34 import org.thingsboard.server.common.data.page.PageLink;
... ... @@ -36,6 +39,8 @@ import java.util.ArrayList;
36 39 import java.util.Collections;
37 40 import java.util.List;
38 41
  42 +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
  43 +
39 44 public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
40 45
41 46 public static final String TITLE = "My firmware";
... ... @@ -50,6 +55,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
50 55
51 56 private TenantId tenantId;
52 57
  58 + private DeviceProfileId deviceProfileId;
  59 +
53 60 @Before
54 61 public void before() {
55 62 Tenant tenant = new Tenant();
... ... @@ -57,8 +64,16 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
57 64 Tenant savedTenant = tenantService.saveTenant(tenant);
58 65 Assert.assertNotNull(savedTenant);
59 66 tenantId = savedTenant.getId();
  67 +
  68 + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Device Profile");
  69 + DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile);
  70 + Assert.assertNotNull(savedDeviceProfile);
  71 + deviceProfileId = savedDeviceProfile.getId();
60 72 }
61 73
  74 + @Rule
  75 + public ExpectedException thrown = ExpectedException.none();
  76 +
62 77 @After
63 78 public void after() {
64 79 tenantService.deleteTenant(tenantId);
... ... @@ -68,6 +83,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
68 83 public void testSaveFirmware() {
69 84 Firmware firmware = new Firmware();
70 85 firmware.setTenantId(tenantId);
  86 + firmware.setDeviceProfileId(deviceProfileId);
  87 + firmware.setType(FIRMWARE);
71 88 firmware.setTitle(TITLE);
72 89 firmware.setVersion(VERSION);
73 90 firmware.setFileName(FILE_NAME);
... ... @@ -99,6 +116,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
99 116 public void testSaveFirmwareInfoAndUpdateWithData() {
100 117 FirmwareInfo firmwareInfo = new FirmwareInfo();
101 118 firmwareInfo.setTenantId(tenantId);
  119 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  120 + firmwareInfo.setType(FIRMWARE);
102 121 firmwareInfo.setTitle(TITLE);
103 122 firmwareInfo.setVersion(VERSION);
104 123 FirmwareInfo savedFirmwareInfo = firmwareService.saveFirmwareInfo(firmwareInfo);
... ... @@ -112,6 +131,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
112 131 Firmware firmware = new Firmware(savedFirmwareInfo.getId());
113 132 firmware.setCreatedTime(firmwareInfo.getCreatedTime());
114 133 firmware.setTenantId(tenantId);
  134 + firmware.setDeviceProfileId(deviceProfileId);
  135 + firmware.setType(FIRMWARE);
115 136 firmware.setTitle(TITLE);
116 137 firmware.setVersion(VERSION);
117 138 firmware.setFileName(FILE_NAME);
... ... @@ -135,9 +156,11 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
135 156 firmwareService.deleteFirmware(tenantId, savedFirmwareInfo.getId());
136 157 }
137 158
138   - @Test(expected = DataValidationException.class)
  159 + @Test
139 160 public void testSaveFirmwareWithEmptyTenant() {
140 161 Firmware firmware = new Firmware();
  162 + firmware.setDeviceProfileId(deviceProfileId);
  163 + firmware.setType(FIRMWARE);
141 164 firmware.setTitle(TITLE);
142 165 firmware.setVersion(VERSION);
143 166 firmware.setFileName(FILE_NAME);
... ... @@ -145,65 +168,108 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
145 168 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
146 169 firmware.setChecksum(CHECKSUM);
147 170 firmware.setData(DATA);
  171 +
  172 + thrown.expect(DataValidationException.class);
  173 + thrown.expectMessage("Firmware should be assigned to tenant!");
148 174 firmwareService.saveFirmware(firmware);
149 175 }
150 176
151   - @Test(expected = DataValidationException.class)
  177 + @Test
  178 + public void testSaveFirmwareWithEmptyType() {
  179 + Firmware firmware = new Firmware();
  180 + firmware.setTenantId(tenantId);
  181 + firmware.setDeviceProfileId(deviceProfileId);
  182 + firmware.setTitle(TITLE);
  183 + firmware.setVersion(VERSION);
  184 + firmware.setFileName(FILE_NAME);
  185 + firmware.setContentType(CONTENT_TYPE);
  186 + firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
  187 + firmware.setChecksum(CHECKSUM);
  188 + firmware.setData(DATA);
  189 +
  190 + thrown.expect(DataValidationException.class);
  191 + thrown.expectMessage("Type should be specified!");
  192 + firmwareService.saveFirmware(firmware);
  193 + }
  194 +
  195 + @Test
152 196 public void testSaveFirmwareWithEmptyTitle() {
153 197 Firmware firmware = new Firmware();
154 198 firmware.setTenantId(tenantId);
  199 + firmware.setDeviceProfileId(deviceProfileId);
  200 + firmware.setType(FIRMWARE);
155 201 firmware.setVersion(VERSION);
156 202 firmware.setFileName(FILE_NAME);
157 203 firmware.setContentType(CONTENT_TYPE);
158 204 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
159 205 firmware.setChecksum(CHECKSUM);
160 206 firmware.setData(DATA);
  207 +
  208 + thrown.expect(DataValidationException.class);
  209 + thrown.expectMessage("Firmware title should be specified!");
161 210 firmwareService.saveFirmware(firmware);
162 211 }
163 212
164   - @Test(expected = DataValidationException.class)
  213 + @Test
165 214 public void testSaveFirmwareWithEmptyFileName() {
166 215 Firmware firmware = new Firmware();
167 216 firmware.setTenantId(tenantId);
  217 + firmware.setDeviceProfileId(deviceProfileId);
  218 + firmware.setType(FIRMWARE);
168 219 firmware.setTitle(TITLE);
169 220 firmware.setVersion(VERSION);
170 221 firmware.setContentType(CONTENT_TYPE);
171 222 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
172 223 firmware.setChecksum(CHECKSUM);
173 224 firmware.setData(DATA);
  225 +
  226 + thrown.expect(DataValidationException.class);
  227 + thrown.expectMessage("Firmware file name should be specified!");
174 228 firmwareService.saveFirmware(firmware);
175 229 }
176 230
177   - @Test(expected = DataValidationException.class)
  231 + @Test
178 232 public void testSaveFirmwareWithEmptyContentType() {
179 233 Firmware firmware = new Firmware();
180 234 firmware.setTenantId(tenantId);
  235 + firmware.setDeviceProfileId(deviceProfileId);
  236 + firmware.setType(FIRMWARE);
181 237 firmware.setTitle(TITLE);
182 238 firmware.setVersion(VERSION);
183 239 firmware.setFileName(FILE_NAME);
184 240 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
185 241 firmware.setChecksum(CHECKSUM);
186 242 firmware.setData(DATA);
  243 +
  244 + thrown.expect(DataValidationException.class);
  245 + thrown.expectMessage("Firmware content type should be specified!");
187 246 firmwareService.saveFirmware(firmware);
188 247 }
189 248
190   - @Test(expected = DataValidationException.class)
  249 + @Test
191 250 public void testSaveFirmwareWithEmptyData() {
192 251 Firmware firmware = new Firmware();
193 252 firmware.setTenantId(tenantId);
  253 + firmware.setDeviceProfileId(deviceProfileId);
  254 + firmware.setType(FIRMWARE);
194 255 firmware.setTitle(TITLE);
195 256 firmware.setVersion(VERSION);
196 257 firmware.setFileName(FILE_NAME);
197 258 firmware.setContentType(CONTENT_TYPE);
198 259 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
199 260 firmware.setChecksum(CHECKSUM);
  261 +
  262 + thrown.expect(DataValidationException.class);
  263 + thrown.expectMessage("Firmware data should be specified!");
200 264 firmwareService.saveFirmware(firmware);
201 265 }
202 266
203   - @Test(expected = DataValidationException.class)
  267 + @Test
204 268 public void testSaveFirmwareWithInvalidTenant() {
205 269 Firmware firmware = new Firmware();
206 270 firmware.setTenantId(new TenantId(Uuids.timeBased()));
  271 + firmware.setDeviceProfileId(deviceProfileId);
  272 + firmware.setType(FIRMWARE);
207 273 firmware.setTitle(TITLE);
208 274 firmware.setVersion(VERSION);
209 275 firmware.setFileName(FILE_NAME);
... ... @@ -211,41 +277,77 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
211 277 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
212 278 firmware.setChecksum(CHECKSUM);
213 279 firmware.setData(DATA);
  280 +
  281 + thrown.expect(DataValidationException.class);
  282 + thrown.expectMessage("Firmware is referencing to non-existent tenant!");
214 283 firmwareService.saveFirmware(firmware);
215 284 }
216 285
217   - @Test(expected = DataValidationException.class)
  286 + @Test
  287 + public void testSaveFirmwareWithInvalidDeviceProfileId() {
  288 + Firmware firmware = new Firmware();
  289 + firmware.setTenantId(tenantId);
  290 + firmware.setDeviceProfileId(new DeviceProfileId(Uuids.timeBased()));
  291 + firmware.setType(FIRMWARE);
  292 + firmware.setTitle(TITLE);
  293 + firmware.setVersion(VERSION);
  294 + firmware.setFileName(FILE_NAME);
  295 + firmware.setContentType(CONTENT_TYPE);
  296 + firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
  297 + firmware.setChecksum(CHECKSUM);
  298 + firmware.setData(DATA);
  299 +
  300 + thrown.expect(DataValidationException.class);
  301 + thrown.expectMessage("Firmware is referencing to non-existent device profile!");
  302 + firmwareService.saveFirmware(firmware);
  303 + }
  304 +
  305 + @Test
218 306 public void testSaveFirmwareWithEmptyChecksum() {
219 307 Firmware firmware = new Firmware();
220   - firmware.setTenantId(new TenantId(Uuids.timeBased()));
  308 + firmware.setTenantId(tenantId);
  309 + firmware.setDeviceProfileId(deviceProfileId);
  310 + firmware.setType(FIRMWARE);
221 311 firmware.setTitle(TITLE);
222 312 firmware.setVersion(VERSION);
223 313 firmware.setFileName(FILE_NAME);
224 314 firmware.setContentType(CONTENT_TYPE);
225 315 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
226 316 firmware.setData(DATA);
  317 +
  318 + thrown.expect(DataValidationException.class);
  319 + thrown.expectMessage("Firmware checksum should be specified!");
227 320 firmwareService.saveFirmware(firmware);
228 321 }
229 322
230   - @Test(expected = DataValidationException.class)
  323 + @Test
231 324 public void testSaveFirmwareInfoWithExistingTitleAndVersion() {
232 325 FirmwareInfo firmwareInfo = new FirmwareInfo();
233 326 firmwareInfo.setTenantId(tenantId);
  327 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  328 + firmwareInfo.setType(FIRMWARE);
234 329 firmwareInfo.setTitle(TITLE);
235 330 firmwareInfo.setVersion(VERSION);
236 331 firmwareService.saveFirmwareInfo(firmwareInfo);
237 332
238 333 FirmwareInfo newFirmwareInfo = new FirmwareInfo();
239 334 newFirmwareInfo.setTenantId(tenantId);
  335 + newFirmwareInfo.setDeviceProfileId(deviceProfileId);
  336 + newFirmwareInfo.setType(FIRMWARE);
240 337 newFirmwareInfo.setTitle(TITLE);
241 338 newFirmwareInfo.setVersion(VERSION);
  339 +
  340 + thrown.expect(DataValidationException.class);
  341 + thrown.expectMessage("Firmware with such title and version already exists!");
242 342 firmwareService.saveFirmwareInfo(newFirmwareInfo);
243 343 }
244 344
245   - @Test(expected = DataValidationException.class)
  345 + @Test
246 346 public void testSaveFirmwareWithExistingTitleAndVersion() {
247 347 Firmware firmware = new Firmware();
248 348 firmware.setTenantId(tenantId);
  349 + firmware.setDeviceProfileId(deviceProfileId);
  350 + firmware.setType(FIRMWARE);
249 351 firmware.setTitle(TITLE);
250 352 firmware.setVersion(VERSION);
251 353 firmware.setFileName(FILE_NAME);
... ... @@ -257,18 +359,27 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
257 359
258 360 Firmware newFirmware = new Firmware();
259 361 newFirmware.setTenantId(tenantId);
  362 + newFirmware.setDeviceProfileId(deviceProfileId);
  363 + newFirmware.setType(FIRMWARE);
260 364 newFirmware.setTitle(TITLE);
261 365 newFirmware.setVersion(VERSION);
262 366 newFirmware.setFileName(FILE_NAME);
263 367 newFirmware.setContentType(CONTENT_TYPE);
  368 + newFirmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
  369 + newFirmware.setChecksum(CHECKSUM);
264 370 newFirmware.setData(DATA);
  371 +
  372 + thrown.expect(DataValidationException.class);
  373 + thrown.expectMessage("Firmware with such title and version already exists!");
265 374 firmwareService.saveFirmware(newFirmware);
266 375 }
267 376
268   - @Test(expected = DataValidationException.class)
  377 + @Test
269 378 public void testDeleteFirmwareWithReferenceByDevice() {
270 379 Firmware firmware = new Firmware();
271 380 firmware.setTenantId(tenantId);
  381 + firmware.setDeviceProfileId(deviceProfileId);
  382 + firmware.setType(FIRMWARE);
272 383 firmware.setTitle(TITLE);
273 384 firmware.setVersion(VERSION);
274 385 firmware.setFileName(FILE_NAME);
... ... @@ -281,11 +392,13 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
281 392 Device device = new Device();
282 393 device.setTenantId(tenantId);
283 394 device.setName("My device");
284   - device.setType("default");
  395 + device.setDeviceProfileId(deviceProfileId);
285 396 device.setFirmwareId(savedFirmware.getId());
286 397 Device savedDevice = deviceService.saveDevice(device);
287 398
288 399 try {
  400 + thrown.expect(DataValidationException.class);
  401 + thrown.expectMessage("The firmware referenced by the devices cannot be deleted!");
289 402 firmwareService.deleteFirmware(tenantId, savedFirmware.getId());
290 403 } finally {
291 404 deviceService.deleteDevice(tenantId, savedDevice.getId());
... ... @@ -293,10 +406,12 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
293 406 }
294 407 }
295 408
296   - @Test(expected = DataValidationException.class)
297   - public void testDeleteFirmwareWithReferenceByDeviceProfile() {
  409 + @Test
  410 + public void testUpdateDeviceProfileIdWithReferenceByDevice() {
298 411 Firmware firmware = new Firmware();
299 412 firmware.setTenantId(tenantId);
  413 + firmware.setDeviceProfileId(deviceProfileId);
  414 + firmware.setType(FIRMWARE);
300 415 firmware.setTitle(TITLE);
301 416 firmware.setVersion(VERSION);
302 417 firmware.setFileName(FILE_NAME);
... ... @@ -306,12 +421,81 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
306 421 firmware.setData(DATA);
307 422 Firmware savedFirmware = firmwareService.saveFirmware(firmware);
308 423
309   - DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Device Profile");
310   - deviceProfile.setFirmwareId(savedFirmware.getId());
  424 + Device device = new Device();
  425 + device.setTenantId(tenantId);
  426 + device.setName("My device");
  427 + device.setDeviceProfileId(deviceProfileId);
  428 + device.setFirmwareId(savedFirmware.getId());
  429 + Device savedDevice = deviceService.saveDevice(device);
  430 +
  431 + try {
  432 + thrown.expect(DataValidationException.class);
  433 + thrown.expectMessage("Can`t update deviceProfileId because firmware is already in use!");
  434 + savedFirmware.setDeviceProfileId(null);
  435 + firmwareService.saveFirmware(savedFirmware);
  436 + } finally {
  437 + deviceService.deleteDevice(tenantId, savedDevice.getId());
  438 + firmwareService.deleteFirmware(tenantId, savedFirmware.getId());
  439 + }
  440 + }
  441 +
  442 + @Test
  443 + public void testDeleteFirmwareWithReferenceByDeviceProfile() {
  444 + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Test Device Profile");
311 445 DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile);
312 446
  447 + Firmware firmware = new Firmware();
  448 + firmware.setTenantId(tenantId);
  449 + firmware.setDeviceProfileId(savedDeviceProfile.getId());
  450 + firmware.setType(FIRMWARE);
  451 + firmware.setTitle(TITLE);
  452 + firmware.setVersion(VERSION);
  453 + firmware.setFileName(FILE_NAME);
  454 + firmware.setContentType(CONTENT_TYPE);
  455 + firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
  456 + firmware.setChecksum(CHECKSUM);
  457 + firmware.setData(DATA);
  458 + Firmware savedFirmware = firmwareService.saveFirmware(firmware);
  459 +
  460 + savedDeviceProfile.setFirmwareId(savedFirmware.getId());
  461 + deviceProfileService.saveDeviceProfile(savedDeviceProfile);
  462 +
313 463 try {
  464 + thrown.expect(DataValidationException.class);
  465 + thrown.expectMessage("The firmware referenced by the device profile cannot be deleted!");
  466 + firmwareService.deleteFirmware(tenantId, savedFirmware.getId());
  467 + } finally {
  468 + deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId());
314 469 firmwareService.deleteFirmware(tenantId, savedFirmware.getId());
  470 + }
  471 + }
  472 +
  473 + @Test
  474 + public void testUpdateDeviceProfileIdWithReferenceByDeviceProfile() {
  475 + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Test Device Profile");
  476 + DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile);
  477 +
  478 + Firmware firmware = new Firmware();
  479 + firmware.setTenantId(tenantId);
  480 + firmware.setDeviceProfileId(savedDeviceProfile.getId());
  481 + firmware.setType(FIRMWARE);
  482 + firmware.setTitle(TITLE);
  483 + firmware.setVersion(VERSION);
  484 + firmware.setFileName(FILE_NAME);
  485 + firmware.setContentType(CONTENT_TYPE);
  486 + firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
  487 + firmware.setChecksum(CHECKSUM);
  488 + firmware.setData(DATA);
  489 + Firmware savedFirmware = firmwareService.saveFirmware(firmware);
  490 +
  491 + savedDeviceProfile.setFirmwareId(savedFirmware.getId());
  492 + deviceProfileService.saveDeviceProfile(savedDeviceProfile);
  493 +
  494 + try {
  495 + thrown.expect(DataValidationException.class);
  496 + thrown.expectMessage("Can`t update deviceProfileId because firmware is already in use!");
  497 + savedFirmware.setDeviceProfileId(null);
  498 + firmwareService.saveFirmware(savedFirmware);
315 499 } finally {
316 500 deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId());
317 501 firmwareService.deleteFirmware(tenantId, savedFirmware.getId());
... ... @@ -322,6 +506,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
322 506 public void testFindFirmwareById() {
323 507 Firmware firmware = new Firmware();
324 508 firmware.setTenantId(tenantId);
  509 + firmware.setDeviceProfileId(deviceProfileId);
  510 + firmware.setType(FIRMWARE);
325 511 firmware.setTitle(TITLE);
326 512 firmware.setVersion(VERSION);
327 513 firmware.setFileName(FILE_NAME);
... ... @@ -341,6 +527,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
341 527 public void testFindFirmwareInfoById() {
342 528 FirmwareInfo firmware = new FirmwareInfo();
343 529 firmware.setTenantId(tenantId);
  530 + firmware.setDeviceProfileId(deviceProfileId);
  531 + firmware.setType(FIRMWARE);
344 532 firmware.setTitle(TITLE);
345 533 firmware.setVersion(VERSION);
346 534 FirmwareInfo savedFirmware = firmwareService.saveFirmwareInfo(firmware);
... ... @@ -355,6 +543,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
355 543 public void testDeleteFirmware() {
356 544 Firmware firmware = new Firmware();
357 545 firmware.setTenantId(tenantId);
  546 + firmware.setDeviceProfileId(deviceProfileId);
  547 + firmware.setType(FIRMWARE);
358 548 firmware.setTitle(TITLE);
359 549 firmware.setVersion(VERSION);
360 550 firmware.setFileName(FILE_NAME);
... ... @@ -377,6 +567,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
377 567 for (int i = 0; i < 165; i++) {
378 568 Firmware firmware = new Firmware();
379 569 firmware.setTenantId(tenantId);
  570 + firmware.setDeviceProfileId(deviceProfileId);
  571 + firmware.setType(FIRMWARE);
380 572 firmware.setTitle(TITLE);
381 573 firmware.setVersion(VERSION + i);
382 574 firmware.setFileName(FILE_NAME);
... ... @@ -420,6 +612,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
420 612 for (int i = 0; i < 165; i++) {
421 613 FirmwareInfo firmwareInfo = new FirmwareInfo();
422 614 firmwareInfo.setTenantId(tenantId);
  615 + firmwareInfo.setDeviceProfileId(deviceProfileId);
  616 + firmwareInfo.setType(FIRMWARE);
423 617 firmwareInfo.setTitle(TITLE);
424 618 firmwareInfo.setVersion(VERSION + i);
425 619 firmwareInfo.setFileName(FILE_NAME);
... ... @@ -434,7 +628,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
434 628 PageLink pageLink = new PageLink(16);
435 629 PageData<FirmwareInfo> pageData;
436 630 do {
437   - pageData = firmwareService.findTenantFirmwaresByTenantIdAndHasData(tenantId, false, pageLink);
  631 + pageData = firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, false, pageLink);
438 632 loadedFirmwares.addAll(pageData.getData());
439 633 if (pageData.hasNext()) {
440 634 pageLink = pageLink.nextPageLink();
... ... @@ -450,6 +644,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
450 644 Firmware firmware = new Firmware(f.getId());
451 645 firmware.setCreatedTime(f.getCreatedTime());
452 646 firmware.setTenantId(f.getTenantId());
  647 + firmware.setDeviceProfileId(deviceProfileId);
  648 + firmware.setType(FIRMWARE);
453 649 firmware.setTitle(f.getTitle());
454 650 firmware.setVersion(f.getVersion());
455 651 firmware.setFileName(FILE_NAME);
... ... @@ -465,7 +661,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
465 661 loadedFirmwares = new ArrayList<>();
466 662 pageLink = new PageLink(16);
467 663 do {
468   - pageData = firmwareService.findTenantFirmwaresByTenantIdAndHasData(tenantId, true, pageLink);
  664 + pageData = firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, true, pageLink);
469 665 loadedFirmwares.addAll(pageData.getData());
470 666 if (pageData.hasNext()) {
471 667 pageLink = pageLink.nextPageLink();
... ...
... ... @@ -361,7 +361,7 @@ export class EntityService {
361 361 break;
362 362 case EntityType.FIRMWARE:
363 363 pageLink.sortOrder.property = 'title';
364   - entitiesObservable = this.firmwareService.getFirmwares(pageLink, true, config);
  364 + entitiesObservable = this.firmwareService.getFirmwares(pageLink, config);
365 365 break;
366 366 }
367 367 return entitiesObservable;
... ...
... ... @@ -20,7 +20,7 @@ import { PageLink } from '@shared/models/page/page-link';
20 20 import { defaultHttpOptionsFromConfig, defaultHttpUploadOptions, RequestConfig } from '@core/http/http-utils';
21 21 import { Observable } from 'rxjs';
22 22 import { PageData } from '@shared/models/page/page-data';
23   -import { Firmware, FirmwareInfo } from '@shared/models/firmware.models';
  23 +import { Firmware, FirmwareInfo, FirmwareType } from '@shared/models/firmware.models';
24 24 import { catchError, map, mergeMap } from 'rxjs/operators';
25 25 import { deepClone, isDefinedAndNotNull } from '@core/utils';
26 26
... ... @@ -34,12 +34,13 @@ export class FirmwareService {
34 34
35 35 }
36 36
37   - public getFirmwares(pageLink: PageLink, hasData?: boolean, config?: RequestConfig): Observable<PageData<FirmwareInfo>> {
38   - let url = `/api/firmwares`;
39   - if (isDefinedAndNotNull(hasData)) {
40   - url += `/${hasData}`;
41   - }
42   - url += `${pageLink.toQuery()}`;
  37 + public getFirmwares(pageLink: PageLink, config?: RequestConfig): Observable<PageData<FirmwareInfo>> {
  38 + return this.http.get<PageData<FirmwareInfo>>(`/api/firmwares${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config));
  39 + }
  40 +
  41 + public getFirmwaresInfoByDeviceProfileId(pageLink: PageLink, deviceProfileId: string, type: FirmwareType,
  42 + hasData = true, config?: RequestConfig): Observable<PageData<FirmwareInfo>> {
  43 + const url = `/api/firmwares/${deviceProfileId}/${type}/${hasData}${pageLink.toQuery()}`;
43 44 return this.http.get<PageData<FirmwareInfo>>(url, defaultHttpOptionsFromConfig(config));
44 45 }
45 46
... ...
... ... @@ -60,10 +60,6 @@
60 60 {{ 'device-profile.type-required' | translate }}
61 61 </mat-error>
62 62 </mat-form-field>
63   - <tb-firmware-autocomplete
64   - [useFullEntityId]="true"
65   - formControlName="firmwareId">
66   - </tb-firmware-autocomplete>
67 63 <mat-form-field class="mat-block">
68 64 <mat-label translate>device-profile.description</mat-label>
69 65 <textarea matInput formControlName="description" rows="2"></textarea>
... ...
... ... @@ -108,7 +108,6 @@ export class AddDeviceProfileDialogComponent extends
108 108 type: [DeviceProfileType.DEFAULT, [Validators.required]],
109 109 defaultRuleChainId: [null, []],
110 110 defaultQueueName: ['', []],
111   - firmwareId: [null],
112 111 description: ['', []]
113 112 }
114 113 );
... ... @@ -187,7 +186,6 @@ export class AddDeviceProfileDialogComponent extends
187 186 transportType: this.transportConfigFormGroup.get('transportType').value,
188 187 provisionType: deviceProvisionConfiguration.type,
189 188 provisionDeviceKey,
190   - firmwareId: this.deviceProfileDetailsFormGroup.get('firmwareId').value,
191 189 description: this.deviceProfileDetailsFormGroup.get('description').value,
192 190 profileData: {
193 191 configuration: createDeviceProfileConfiguration(DeviceProfileType.DEFAULT),
... ...
... ... @@ -65,8 +65,16 @@
65 65 </tb-queue-type-list>
66 66 <tb-firmware-autocomplete
67 67 [useFullEntityId]="true"
  68 + [type]="firmwareTypes.FIRMWARE"
  69 + [deviceProfileId]="deviceProfileId?.id"
68 70 formControlName="firmwareId">
69 71 </tb-firmware-autocomplete>
  72 + <tb-firmware-autocomplete
  73 + [useFullEntityId]="true"
  74 + [type]="firmwareTypes.SOFTWARE"
  75 + [deviceProfileId]="deviceProfileId?.id"
  76 + formControlName="softwareId">
  77 + </tb-firmware-autocomplete>
70 78 <mat-form-field fxHide class="mat-block">
71 79 <mat-label translate>device-profile.type</mat-label>
72 80 <mat-select formControlName="type" required>
... ...
... ... @@ -40,6 +40,7 @@ import { EntityType } from '@shared/models/entity-type.models';
40 40 import { RuleChainId } from '@shared/models/id/rule-chain-id';
41 41 import { ServiceType } from '@shared/models/queue.models';
42 42 import { EntityId } from '@shared/models/id/entity-id';
  43 +import { FirmwareType } from '@shared/models/firmware.models';
43 44
44 45 @Component({
45 46 selector: 'tb-device-profile',
... ... @@ -69,6 +70,8 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
69 70
70 71 deviceProfileId: EntityId;
71 72
  73 + firmwareTypes = FirmwareType;
  74 +
72 75 constructor(protected store: Store<AppState>,
73 76 protected translate: TranslateService,
74 77 @Optional() @Inject('entity') protected entityValue: DeviceProfile,
... ... @@ -110,6 +113,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
110 113 defaultRuleChainId: [entity && entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null, []],
111 114 defaultQueueName: [entity ? entity.defaultQueueName : '', []],
112 115 firmwareId: [entity ? entity.firmwareId : null],
  116 + softwareId: [entity ? entity.softwareId : null],
113 117 description: [entity ? entity.description : '', []],
114 118 }
115 119 );
... ... @@ -186,6 +190,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
186 190 this.entityForm.patchValue({defaultRuleChainId: entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null}, {emitEvent: false});
187 191 this.entityForm.patchValue({defaultQueueName: entity.defaultQueueName}, {emitEvent: false});
188 192 this.entityForm.patchValue({firmwareId: entity.firmwareId}, {emitEvent: false});
  193 + this.entityForm.patchValue({softwareId: entity.softwareId}, {emitEvent: false});
189 194 this.entityForm.patchValue({description: entity.description}, {emitEvent: false});
190 195 }
191 196
... ...
... ... @@ -48,10 +48,6 @@
48 48 <mat-label translate>device.label</mat-label>
49 49 <input matInput formControlName="label">
50 50 </mat-form-field>
51   - <tb-firmware-autocomplete
52   - [useFullEntityId]="true"
53   - formControlName="firmwareId">
54   - </tb-firmware-autocomplete>
55 51 <mat-form-field class="mat-block" style="padding-bottom: 14px;">
56 52 <mat-label translate>device-profile.transport-type</mat-label>
57 53 <mat-select formControlName="transportType" required>
... ...
... ... @@ -107,7 +107,6 @@ export class DeviceWizardDialogComponent extends
107 107 this.deviceWizardFormGroup = this.fb.group({
108 108 name: ['', Validators.required],
109 109 label: [''],
110   - firmwareId: [null],
111 110 gateway: [false],
112 111 overwriteActivityTime: [false],
113 112 transportType: [DeviceTransportType.DEFAULT, Validators.required],
... ... @@ -313,7 +312,6 @@ export class DeviceWizardDialogComponent extends
313 312 const device = {
314 313 name: this.deviceWizardFormGroup.get('name').value,
315 314 label: this.deviceWizardFormGroup.get('label').value,
316   - firmwareId: this.deviceWizardFormGroup.get('firmwareId').value,
317 315 deviceProfileId: profileId,
318 316 additionalInfo: {
319 317 gateway: this.deviceWizardFormGroup.get('gateway').value,
... ...
... ... @@ -103,8 +103,16 @@
103 103 </mat-form-field>
104 104 <tb-firmware-autocomplete
105 105 [useFullEntityId]="true"
  106 + [type]="firmwareTypes.FIRMWARE"
  107 + [deviceProfileId]="entityForm.get('deviceProfileId').value?.id"
106 108 formControlName="firmwareId">
107 109 </tb-firmware-autocomplete>
  110 + <tb-firmware-autocomplete
  111 + [useFullEntityId]="true"
  112 + [type]="firmwareTypes.SOFTWARE"
  113 + [deviceProfileId]="entityForm.get('deviceProfileId').value?.id"
  114 + formControlName="softwareId">
  115 + </tb-firmware-autocomplete>
108 116 <tb-device-data
109 117 formControlName="deviceData"
110 118 required>
... ...
... ... @@ -34,6 +34,7 @@ import { ActionNotificationShow } from '@core/notification/notification.actions'
34 34 import { TranslateService } from '@ngx-translate/core';
35 35 import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
36 36 import { Subject } from 'rxjs';
  37 +import { FirmwareType } from '@shared/models/firmware.models';
37 38
38 39 @Component({
39 40 selector: 'tb-device',
... ... @@ -48,6 +49,8 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> {
48 49
49 50 deviceScope: 'tenant' | 'customer' | 'customer_user' | 'edge';
50 51
  52 + firmwareTypes = FirmwareType;
  53 +
51 54 constructor(protected store: Store<AppState>,
52 55 protected translate: TranslateService,
53 56 @Inject('entity') protected entityValue: DeviceInfo,
... ... @@ -80,6 +83,7 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> {
80 83 name: [entity ? entity.name : '', [Validators.required]],
81 84 deviceProfileId: [entity ? entity.deviceProfileId : null, [Validators.required]],
82 85 firmwareId: [entity ? entity.firmwareId : null],
  86 + softwareId: [entity ? entity.softwareId : null],
83 87 label: [entity ? entity.label : ''],
84 88 deviceData: [entity ? entity.deviceData : null, [Validators.required]],
85 89 additionalInfo: this.fb.group(
... ... @@ -94,19 +98,19 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> {
94 98 }
95 99
96 100 updateForm(entity: DeviceInfo) {
97   - this.entityForm.patchValue({name: entity.name});
98   - this.entityForm.patchValue({deviceProfileId: entity.deviceProfileId});
99   - this.entityForm.patchValue({firmwareId: entity.firmwareId});
100   - this.entityForm.patchValue({label: entity.label});
101   - this.entityForm.patchValue({deviceData: entity.deviceData});
102 101 this.entityForm.patchValue({
103   - additionalInfo:
104   - {
105   - gateway: entity.additionalInfo ? entity.additionalInfo.gateway : false,
106   - overwriteActivityTime: entity.additionalInfo ? entity.additionalInfo.overwriteActivityTime : false
107   - }
  102 + name: entity.name,
  103 + deviceProfileId: entity.deviceProfileId,
  104 + firmwareId: entity.firmwareId,
  105 + softwareId: entity.softwareId,
  106 + label: entity.label,
  107 + deviceData: entity.deviceData,
  108 + additionalInfo: {
  109 + gateway: entity.additionalInfo ? entity.additionalInfo.gateway : false,
  110 + overwriteActivityTime: entity.additionalInfo ? entity.additionalInfo.overwriteActivityTime : false,
  111 + description: entity.additionalInfo ? entity.additionalInfo.description : ''
  112 + }
108 113 });
109   - this.entityForm.patchValue({additionalInfo: {description: entity.additionalInfo ? entity.additionalInfo.description : ''}});
110 114 }
111 115
112 116
... ... @@ -152,6 +156,10 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> {
152 156 this.entityForm.markAsDirty();
153 157 }
154 158 }
  159 + this.entityForm.patchValue({
  160 + firmwareId: null,
  161 + softwareId: null
  162 + });
155 163 }
156 164 }
157 165 }
... ...
... ... @@ -21,7 +21,12 @@ import {
21 21 EntityTableColumn,
22 22 EntityTableConfig
23 23 } from '@home/models/entity/entities-table-config.models';
24   -import { Firmware, FirmwareInfo } from '@shared/models/firmware.models';
  24 +import {
  25 + ChecksumAlgorithmTranslationMap,
  26 + Firmware,
  27 + FirmwareInfo,
  28 + FirmwareTypeTranslationMap
  29 +} from '@shared/models/firmware.models';
25 30 import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models';
26 31 import { TranslateService } from '@ngx-translate/core';
27 32 import { DatePipe } from '@angular/common';
... ... @@ -49,14 +54,17 @@ export class FirmwareTableConfigResolve implements Resolve<EntityTableConfig<Fir
49 54
50 55 this.config.columns.push(
51 56 new DateEntityTableColumn<FirmwareInfo>('createdTime', 'common.created-time', this.datePipe, '150px'),
52   - new EntityTableColumn<FirmwareInfo>('title', 'firmware.title', '33%'),
53   - new EntityTableColumn<FirmwareInfo>('version', 'firmware.version', '33%'),
54   - new EntityTableColumn<FirmwareInfo>('fileName', 'firmware.file-name', '33%'),
  57 + new EntityTableColumn<FirmwareInfo>('title', 'firmware.title', '25%'),
  58 + new EntityTableColumn<FirmwareInfo>('version', 'firmware.version', '25%'),
  59 + new EntityTableColumn<FirmwareInfo>('type', 'firmware.type', '25%', entity => {
  60 + return this.translate.instant(FirmwareTypeTranslationMap.get(entity.type));
  61 + }),
  62 + new EntityTableColumn<FirmwareInfo>('fileName', 'firmware.file-name', '25%'),
55 63 new EntityTableColumn<FirmwareInfo>('dataSize', 'firmware.file-size', '70px', entity => {
56 64 return this.fileSize.transform(entity.dataSize || 0);
57 65 }),
58 66 new EntityTableColumn<FirmwareInfo>('checksum', 'firmware.checksum', '540px', entity => {
59   - return `${entity.checksumAlgorithm}: ${entity.checksum}`;
  67 + return `${ChecksumAlgorithmTranslationMap.get(entity.checksumAlgorithm)}: ${entity.checksum}`;
60 68 }, () => ({}), false)
61 69 );
62 70
... ...
... ... @@ -67,10 +67,27 @@
67 67 </mat-error>
68 68 </mat-form-field>
69 69 </div>
  70 + <div fxLayout="row" fxLayoutGap.gt-xs="8px" fxLayout.xs="column">
  71 + <mat-form-field fxFlex="45">
  72 + <mat-label translate>firmware.type</mat-label>
  73 + <input *ngIf="!isAdd" matInput type="text" [readonly]="isEdit" [disabled]="!isEdit"
  74 + value="{{ firmwareTypeTranslationMap.get(entityForm.get('type').value) | translate }}">
  75 + <mat-select formControlName="type" required *ngIf="isAdd">
  76 + <mat-option *ngFor="let firmwareType of firmwareTypes" [value]="firmwareType">
  77 + {{ firmwareTypeTranslationMap.get(firmwareType) | translate }}
  78 + </mat-option>
  79 + </mat-select>
  80 + </mat-form-field>
  81 + <tb-device-profile-autocomplete
  82 + formControlName="deviceProfileId" fxFlex
  83 + [editProfileEnabled]="false">
  84 + </tb-device-profile-autocomplete>
  85 + </div>
70 86 <div fxLayout="row" fxLayoutGap.gt-xs="8px" fxLayoutGap.sm="8px" fxLayout.xs="column" fxLayout.md="column">
71 87 <mat-form-field class="mat-block" fxFlex="33">
72 88 <mat-label translate>firmware.checksum-algorithm</mat-label>
73   - <input *ngIf="!isAdd" matInput formControlName="checksumAlgorithm" type="text" [readonly]="isEdit">
  89 + <input *ngIf="!isAdd" matInput type="text" [readonly]="isEdit" [disabled]="!isEdit"
  90 + value="{{ checksumAlgorithmTranslationMap.get(entityForm.get('checksumAlgorithm').value) | translate }}">
74 91 <mat-select formControlName="checksumAlgorithm" *ngIf="isAdd">
75 92 <mat-option [value]=null></mat-option>
76 93 <mat-option *ngFor="let checksumAlgorithm of checksumAlgorithms" [value]="checksumAlgorithm">
... ...
... ... @@ -22,7 +22,13 @@ import { TranslateService } from '@ngx-translate/core';
22 22 import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
23 23 import { FormBuilder, FormGroup, Validators } from '@angular/forms';
24 24 import { EntityComponent } from '@home/components/entity/entity.component';
25   -import { ChecksumAlgorithm, ChecksumAlgorithmTranslationMap, Firmware } from '@shared/models/firmware.models';
  25 +import {
  26 + ChecksumAlgorithm,
  27 + ChecksumAlgorithmTranslationMap,
  28 + Firmware,
  29 + FirmwareType,
  30 + FirmwareTypeTranslationMap
  31 +} from '@shared/models/firmware.models';
26 32 import { distinctUntilChanged, map, takeUntil } from 'rxjs/operators';
27 33 import { ActionNotificationShow } from '@core/notification/notification.actions';
28 34
... ... @@ -36,6 +42,8 @@ export class FirmwaresComponent extends EntityComponent<Firmware> implements OnI
36 42
37 43 checksumAlgorithms = Object.values(ChecksumAlgorithm);
38 44 checksumAlgorithmTranslationMap = ChecksumAlgorithmTranslationMap;
  45 + firmwareTypes = Object.values(FirmwareType);
  46 + firmwareTypeTranslationMap = FirmwareTypeTranslationMap;
39 47
40 48 constructor(protected store: Store<AppState>,
41 49 protected translate: TranslateService,
... ... @@ -83,6 +91,8 @@ export class FirmwaresComponent extends EntityComponent<Firmware> implements OnI
83 91 const form = this.fb.group({
84 92 title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]],
85 93 version: [entity ? entity.version : '', [Validators.required, Validators.maxLength(255)]],
  94 + type: [entity?.type ? entity.type : FirmwareType.FIRMWARE, [Validators.required]],
  95 + deviceProfileId: [entity ? entity.deviceProfileId : null],
86 96 checksumAlgorithm: [entity ? entity.checksumAlgorithm : null],
87 97 checksum: [entity ? entity.checksum : '', Validators.maxLength(1020)],
88 98 additionalInfo: this.fb.group(
... ... @@ -105,6 +115,8 @@ export class FirmwaresComponent extends EntityComponent<Firmware> implements OnI
105 115 this.entityForm.patchValue({
106 116 title: entity.title,
107 117 version: entity.version,
  118 + type: entity.type,
  119 + deviceProfileId: entity.deviceProfileId,
108 120 checksumAlgorithm: entity.checksumAlgorithm,
109 121 checksum: entity.checksum,
110 122 fileName: entity.fileName,
... ...
... ... @@ -37,11 +37,11 @@
37 37 <mat-option *ngIf="!(filteredFirmwares | async)?.length" [value]="null" class="tb-not-found">
38 38 <div class="tb-not-found-content" (click)="$event.stopPropagation()">
39 39 <div *ngIf="!textIsNotEmpty(searchText); else searchNotEmpty">
40   - <span translate>firmware.no-firmware-text</span>
  40 + <span>{{ notFoundFirmware | translate }}</span>
41 41 </div>
42 42 <ng-template #searchNotEmpty>
43 43 <span>
44   - {{ translate.get('firmware.no-firmware-matching',
  44 + {{ translate.get(notMatchingFirmware,
45 45 {entity: truncate.transform(searchText, true, 6, &apos;...&apos;)}) | async }}
46 46 </span>
47 47 </ng-template>
... ...
... ... @@ -28,7 +28,7 @@ import { BaseData } from '@shared/models/base-data';
28 28 import { EntityService } from '@core/http/entity.service';
29 29 import { TruncatePipe } from '@shared/pipe/truncate.pipe';
30 30 import { MatAutocompleteTrigger } from '@angular/material/autocomplete';
31   -import { FirmwareInfo } from '@shared/models/firmware.models';
  31 +import { FirmwareInfo, FirmwareType } from '@shared/models/firmware.models';
32 32 import { FirmwareService } from '@core/http/firmware.service';
33 33 import { PageLink } from '@shared/models/page/page-link';
34 34 import { Direction } from '@shared/models/page/sort-order';
... ... @@ -50,6 +50,12 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn
50 50 modelValue: string | null;
51 51
52 52 @Input()
  53 + type = FirmwareType.FIRMWARE;
  54 +
  55 + @Input()
  56 + deviceProfileId: string;
  57 +
  58 + @Input()
53 59 labelText: string;
54 60
55 61 @Input()
... ... @@ -81,6 +87,23 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn
81 87
82 88 private dirty = false;
83 89
  90 + private firmwareTypeTranslation = new Map<FirmwareType, any>(
  91 + [
  92 + [FirmwareType.FIRMWARE, {
  93 + label: 'firmware.firmware',
  94 + required: 'firmware.firmware-required',
  95 + noFound: 'firmware.no-firmware-text',
  96 + noMatching: 'firmware.no-firmware-matching'
  97 + }],
  98 + [FirmwareType.SOFTWARE, {
  99 + label: 'firmware.software',
  100 + required: 'firmware.software-required',
  101 + noFound: 'firmware.no-software-text',
  102 + noMatching: 'firmware.no-software-matching'
  103 + }]
  104 + ]
  105 + );
  106 +
84 107 private propagateChange = (v: any) => { };
85 108
86 109 constructor(private store: Store<AppState>,
... ... @@ -160,7 +183,7 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn
160 183 this.entityService.getEntity(EntityType.FIRMWARE, firmwareId, {ignoreLoading: true, ignoreErrors: true}).subscribe(
161 184 (entity) => {
162 185 this.modelValue = entity.id.id;
163   - this.firmwareFormGroup.get('firmwareId').patchValue(entity, {emitEvent: false});
  186 + this.firmwareFormGroup.get('firmwareId').patchValue(entity);
164 187 },
165 188 () => {
166 189 this.modelValue = null;
... ... @@ -173,6 +196,7 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn
173 196 } else {
174 197 this.modelValue = null;
175 198 this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: false});
  199 + this.propagateChange(null);
176 200 }
177 201 } else {
178 202 this.modelValue = null;
... ... @@ -209,7 +233,8 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn
209 233 property: 'title',
210 234 direction: Direction.ASC
211 235 });
212   - return this.firmwareService.getFirmwares(pageLink, true, {ignoreLoading: true}).pipe(
  236 + return this.firmwareService.getFirmwaresInfoByDeviceProfileId(pageLink, this.deviceProfileId, this.type,
  237 + true, {ignoreLoading: true}).pipe(
213 238 map((data) => data && data.data.length ? data.data : null)
214 239 );
215 240 }
... ... @@ -223,11 +248,19 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn
223 248 }
224 249
225 250 get placeholderText(): string {
226   - return this.labelText || 'firmware.firmware';
  251 + return this.labelText || this.firmwareTypeTranslation.get(this.type).label;
227 252 }
228 253
229 254 get requiredErrorText(): string {
230   - return this.requiredText || 'firmware.firmware-required';
  255 + return this.requiredText || this.firmwareTypeTranslation.get(this.type).required;
  256 + }
  257 +
  258 + get notFoundFirmware(): string {
  259 + return this.firmwareTypeTranslation.get(this.type).noFound;
  260 + }
  261 +
  262 + get notMatchingFirmware(): string {
  263 + return this.firmwareTypeTranslation.get(this.type).noMatching;
231 264 }
232 265
233 266 firmwareTitleText(firmware: FirmwareInfo): string {
... ...
... ... @@ -498,6 +498,7 @@ export interface DeviceProfile extends BaseData<DeviceProfileId> {
498 498 defaultRuleChainId?: RuleChainId;
499 499 defaultQueueName?: string;
500 500 firmwareId?: FirmwareId;
  501 + softwareId?: FirmwareId;
501 502 profileData: DeviceProfileData;
502 503 }
503 504
... ... @@ -558,6 +559,7 @@ export interface Device extends BaseData<DeviceId> {
558 559 type: string;
559 560 label: string;
560 561 firmwareId?: FirmwareId;
  562 + softwareId?: FirmwareId;
561 563 deviceProfileId?: DeviceProfileId;
562 564 deviceData?: DeviceData;
563 565 additionalInfo?: any;
... ...
... ... @@ -17,6 +17,7 @@
17 17 import { BaseData } from '@shared/models/base-data';
18 18 import { TenantId } from '@shared/models/id/tenant-id';
19 19 import { FirmwareId } from '@shared/models/id/firmware-id';
  20 +import { DeviceProfileId } from '@shared/models/id/device-profile-id';
20 21
21 22 export enum ChecksumAlgorithm {
22 23 MD5 = 'md5',
... ... @@ -32,14 +33,28 @@ export const ChecksumAlgorithmTranslationMap = new Map<ChecksumAlgorithm, string
32 33 ]
33 34 );
34 35
  36 +export enum FirmwareType {
  37 + FIRMWARE = 'FIRMWARE',
  38 + SOFTWARE = 'SOFTWARE'
  39 +}
  40 +
  41 +export const FirmwareTypeTranslationMap = new Map<FirmwareType, string>(
  42 + [
  43 + [FirmwareType.FIRMWARE, 'firmware.types.firmware'],
  44 + [FirmwareType.SOFTWARE, 'firmware.types.software']
  45 + ]
  46 +);
  47 +
35 48 export interface FirmwareInfo extends BaseData<FirmwareId> {
36 49 tenantId?: TenantId;
  50 + type: FirmwareType;
  51 + deviceProfileId?: DeviceProfileId;
37 52 title?: string;
38 53 version?: string;
39 54 hasData?: boolean;
40 55 fileName: string;
41   - checksum?: ChecksumAlgorithm;
42   - checksumAlgorithm?: string;
  56 + checksum?: string;
  57 + checksumAlgorithm?: ChecksumAlgorithm;
43 58 contentType: string;
44 59 dataSize?: number;
45 60 additionalInfo?: any;
... ...
... ... @@ -1948,6 +1948,8 @@
1948 1948 "idCopiedMessage": "Firmware Id has been copied to clipboard",
1949 1949 "no-firmware-matching": "No firmware matching '{{entity}}' were found.",
1950 1950 "no-firmware-text": "No firmwares found",
  1951 + "no-software-matching": "No sowtware matching '{{entity}}' were found.",
  1952 + "no-software-text": "No software found",
1951 1953 "file-name": "File name",
1952 1954 "file-size": "File size",
1953 1955 "file-size-bytes": "File size in bytes",
... ... @@ -1956,8 +1958,15 @@
1956 1958 "firmware-required": "Firmware is required.",
1957 1959 "search": "Search firmwares",
1958 1960 "selected-firmware": "{ count, plural, 1 {1 firmware} other {# firmwares} } selected",
  1961 + "software": "Software",
  1962 + "software-required": "Software is required.",
1959 1963 "title": "Title",
1960 1964 "title-required": "Title is required.",
  1965 + "type": "Firmware type",
  1966 + "types": {
  1967 + "firmware": "Firmware",
  1968 + "software": "Software"
  1969 + },
1961 1970 "version": "Version",
1962 1971 "version-required": "Version is required.",
1963 1972 "warning-after-save-no-edit": "Once the firmware is saved, it will not be possible to change the title and version fields."
... ...