Commit 1f6210197c07da752622ddeecfe10a66a8eddf34

Authored by Andrew Shvayka
Committed by GitHub
2 parents 99b19034 c7274b61

Merge pull request #4959 from YevhenBondarenko/feature/ota-tag

[3.3.0] OtaPackage Tag
Showing 21 changed files with 185 additions and 108 deletions
@@ -67,6 +67,7 @@ CREATE TABLE IF NOT EXISTS ota_package ( @@ -67,6 +67,7 @@ CREATE TABLE IF NOT EXISTS ota_package (
67 type varchar(32) NOT NULL, 67 type varchar(32) NOT NULL,
68 title varchar(255) NOT NULL, 68 title varchar(255) NOT NULL,
69 version varchar(255) NOT NULL, 69 version varchar(255) NOT NULL,
  70 + tag varchar(255),
70 url varchar(255), 71 url varchar(255),
71 file_name varchar(255), 72 file_name varchar(255),
72 content_type varchar(255), 73 content_type varchar(255),
@@ -146,6 +146,7 @@ public class OtaPackageController extends BaseController { @@ -146,6 +146,7 @@ public class OtaPackageController extends BaseController {
146 otaPackage.setType(info.getType()); 146 otaPackage.setType(info.getType());
147 otaPackage.setTitle(info.getTitle()); 147 otaPackage.setTitle(info.getTitle());
148 otaPackage.setVersion(info.getVersion()); 148 otaPackage.setVersion(info.getVersion());
  149 + otaPackage.setTag(info.getTag());
149 otaPackage.setAdditionalInfo(info.getAdditionalInfo()); 150 otaPackage.setAdditionalInfo(info.getAdditionalInfo());
150 151
151 ChecksumAlgorithm checksumAlgorithm = ChecksumAlgorithm.valueOf(checksumAlgorithmStr.toUpperCase()); 152 ChecksumAlgorithm checksumAlgorithm = ChecksumAlgorithm.valueOf(checksumAlgorithmStr.toUpperCase());
@@ -64,6 +64,7 @@ import static org.thingsboard.server.common.data.ota.OtaPackageKey.CHECKSUM; @@ -64,6 +64,7 @@ import static org.thingsboard.server.common.data.ota.OtaPackageKey.CHECKSUM;
64 import static org.thingsboard.server.common.data.ota.OtaPackageKey.CHECKSUM_ALGORITHM; 64 import static org.thingsboard.server.common.data.ota.OtaPackageKey.CHECKSUM_ALGORITHM;
65 import static org.thingsboard.server.common.data.ota.OtaPackageKey.SIZE; 65 import static org.thingsboard.server.common.data.ota.OtaPackageKey.SIZE;
66 import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE; 66 import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE;
  67 +import static org.thingsboard.server.common.data.ota.OtaPackageKey.TAG;
67 import static org.thingsboard.server.common.data.ota.OtaPackageKey.TITLE; 68 import static org.thingsboard.server.common.data.ota.OtaPackageKey.TITLE;
68 import static org.thingsboard.server.common.data.ota.OtaPackageKey.TS; 69 import static org.thingsboard.server.common.data.ota.OtaPackageKey.TS;
69 import static org.thingsboard.server.common.data.ota.OtaPackageKey.URL; 70 import static org.thingsboard.server.common.data.ota.OtaPackageKey.URL;
@@ -246,6 +247,11 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { @@ -246,6 +247,11 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService {
246 List<TsKvEntry> telemetry = new ArrayList<>(); 247 List<TsKvEntry> telemetry = new ArrayList<>();
247 telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), TITLE), firmware.getTitle()))); 248 telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), TITLE), firmware.getTitle())));
248 telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), VERSION), firmware.getVersion()))); 249 telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), VERSION), firmware.getVersion())));
  250 +
  251 + if (StringUtils.isNotEmpty(firmware.getTag())) {
  252 + telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), TAG), firmware.getTag())));
  253 + }
  254 +
249 telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(getTargetTelemetryKey(firmware.getType(), TS), ts))); 255 telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(getTargetTelemetryKey(firmware.getType(), TS), ts)));
250 telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), OtaPackageUpdateStatus.QUEUED.name()))); 256 telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), OtaPackageUpdateStatus.QUEUED.name())));
251 257
@@ -289,6 +295,9 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { @@ -289,6 +295,9 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService {
289 List<AttributeKvEntry> attributes = new ArrayList<>(); 295 List<AttributeKvEntry> attributes = new ArrayList<>();
290 attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, TITLE), otaPackage.getTitle()))); 296 attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, TITLE), otaPackage.getTitle())));
291 attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, VERSION), otaPackage.getVersion()))); 297 attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, VERSION), otaPackage.getVersion())));
  298 + if (StringUtils.isNotEmpty(otaPackage.getTag())) {
  299 + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, TAG), otaPackage.getTag())));
  300 + }
292 if (otaPackage.hasUrl()) { 301 if (otaPackage.hasUrl()) {
293 attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, URL), otaPackage.getUrl()))); 302 attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, URL), otaPackage.getUrl())));
294 List<String> attrToRemove = new ArrayList<>(); 303 List<String> attrToRemove = new ArrayList<>();
@@ -37,6 +37,7 @@ public class OtaPackageInfo extends SearchTextBasedWithAdditionalInfo<OtaPackage @@ -37,6 +37,7 @@ public class OtaPackageInfo extends SearchTextBasedWithAdditionalInfo<OtaPackage
37 private OtaPackageType type; 37 private OtaPackageType type;
38 private String title; 38 private String title;
39 private String version; 39 private String version;
  40 + private String tag;
40 private String url; 41 private String url;
41 private boolean hasData; 42 private boolean hasData;
42 private String fileName; 43 private String fileName;
@@ -61,6 +62,7 @@ public class OtaPackageInfo extends SearchTextBasedWithAdditionalInfo<OtaPackage @@ -61,6 +62,7 @@ public class OtaPackageInfo extends SearchTextBasedWithAdditionalInfo<OtaPackage
61 this.type = otaPackageInfo.getType(); 62 this.type = otaPackageInfo.getType();
62 this.title = otaPackageInfo.getTitle(); 63 this.title = otaPackageInfo.getTitle();
63 this.version = otaPackageInfo.getVersion(); 64 this.version = otaPackageInfo.getVersion();
  65 + this.tag = otaPackageInfo.getTag();
64 this.url = otaPackageInfo.getUrl(); 66 this.url = otaPackageInfo.getUrl();
65 this.hasData = otaPackageInfo.isHasData(); 67 this.hasData = otaPackageInfo.isHasData();
66 this.fileName = otaPackageInfo.getFileName(); 68 this.fileName = otaPackageInfo.getFileName();
@@ -19,7 +19,7 @@ import lombok.Getter; @@ -19,7 +19,7 @@ import lombok.Getter;
19 19
20 public enum OtaPackageKey { 20 public enum OtaPackageKey {
21 21
22 - TITLE("title"), VERSION("version"), TS("ts"), STATE("state"), SIZE("size"), CHECKSUM("checksum"), CHECKSUM_ALGORITHM("checksum_algorithm"), URL("url"); 22 + TITLE("title"), VERSION("version"), TS("ts"), STATE("state"), SIZE("size"), CHECKSUM("checksum"), CHECKSUM_ALGORITHM("checksum_algorithm"), URL("url"), TAG("tag");
23 23
24 @Getter 24 @Getter
25 private final String value; 25 private final String value;
@@ -120,9 +120,11 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService { @@ -120,9 +120,11 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService {
120 if (msg.getSharedUpdatedCount() > 0 && lwM2MClient != null) { 120 if (msg.getSharedUpdatedCount() > 0 && lwM2MClient != null) {
121 String newFirmwareTitle = null; 121 String newFirmwareTitle = null;
122 String newFirmwareVersion = null; 122 String newFirmwareVersion = null;
  123 + String newFirmwareTag = null;
123 String newFirmwareUrl = null; 124 String newFirmwareUrl = null;
124 String newSoftwareTitle = null; 125 String newSoftwareTitle = null;
125 String newSoftwareVersion = null; 126 String newSoftwareVersion = null;
  127 + String newSoftwareTag = null;
126 String newSoftwareUrl = null; 128 String newSoftwareUrl = null;
127 List<TransportProtos.TsKvProto> otherAttributes = new ArrayList<>(); 129 List<TransportProtos.TsKvProto> otherAttributes = new ArrayList<>();
128 for (TransportProtos.TsKvProto tsKvProto : msg.getSharedUpdatedList()) { 130 for (TransportProtos.TsKvProto tsKvProto : msg.getSharedUpdatedList()) {
@@ -131,12 +133,16 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService { @@ -131,12 +133,16 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService {
131 newFirmwareTitle = getStrValue(tsKvProto); 133 newFirmwareTitle = getStrValue(tsKvProto);
132 } else if (DefaultLwM2MOtaUpdateService.FIRMWARE_VERSION.equals(attrName)) { 134 } else if (DefaultLwM2MOtaUpdateService.FIRMWARE_VERSION.equals(attrName)) {
133 newFirmwareVersion = getStrValue(tsKvProto); 135 newFirmwareVersion = getStrValue(tsKvProto);
  136 + } else if (DefaultLwM2MOtaUpdateService.FIRMWARE_TAG.equals(attrName)) {
  137 + newFirmwareTag = getStrValue(tsKvProto);
134 } else if (DefaultLwM2MOtaUpdateService.FIRMWARE_URL.equals(attrName)) { 138 } else if (DefaultLwM2MOtaUpdateService.FIRMWARE_URL.equals(attrName)) {
135 newFirmwareUrl = getStrValue(tsKvProto); 139 newFirmwareUrl = getStrValue(tsKvProto);
136 } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_TITLE.equals(attrName)) { 140 } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_TITLE.equals(attrName)) {
137 newSoftwareTitle = getStrValue(tsKvProto); 141 newSoftwareTitle = getStrValue(tsKvProto);
138 } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_VERSION.equals(attrName)) { 142 } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_VERSION.equals(attrName)) {
139 newSoftwareVersion = getStrValue(tsKvProto); 143 newSoftwareVersion = getStrValue(tsKvProto);
  144 + } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_TAG.equals(attrName)) {
  145 + newSoftwareTag = getStrValue(tsKvProto);
140 } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_URL.equals(attrName)) { 146 } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_URL.equals(attrName)) {
141 newSoftwareUrl = getStrValue(tsKvProto); 147 newSoftwareUrl = getStrValue(tsKvProto);
142 }else { 148 }else {
@@ -144,10 +150,10 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService { @@ -144,10 +150,10 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService {
144 } 150 }
145 } 151 }
146 if (newFirmwareTitle != null || newFirmwareVersion != null) { 152 if (newFirmwareTitle != null || newFirmwareVersion != null) {
147 - otaUpdateService.onTargetFirmwareUpdate(lwM2MClient, newFirmwareTitle, newFirmwareVersion, Optional.ofNullable(newFirmwareUrl)); 153 + otaUpdateService.onTargetFirmwareUpdate(lwM2MClient, newFirmwareTitle, newFirmwareVersion, Optional.ofNullable(newFirmwareUrl), Optional.ofNullable(newFirmwareTag));
148 } 154 }
149 if (newSoftwareTitle != null || newSoftwareVersion != null) { 155 if (newSoftwareTitle != null || newSoftwareVersion != null) {
150 - otaUpdateService.onTargetSoftwareUpdate(lwM2MClient, newSoftwareTitle, newSoftwareVersion, Optional.ofNullable(newSoftwareUrl)); 156 + otaUpdateService.onTargetSoftwareUpdate(lwM2MClient, newSoftwareTitle, newSoftwareVersion, Optional.ofNullable(newSoftwareUrl), Optional.ofNullable(newSoftwareTag));
151 } 157 }
152 if (!otherAttributes.isEmpty()) { 158 if (!otherAttributes.isEmpty()) {
153 onAttributesUpdate(lwM2MClient, otherAttributes); 159 onAttributesUpdate(lwM2MClient, otherAttributes);
@@ -85,9 +85,11 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl @@ -85,9 +85,11 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
85 85
86 public static final String FIRMWARE_VERSION = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION); 86 public static final String FIRMWARE_VERSION = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION);
87 public static final String FIRMWARE_TITLE = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE); 87 public static final String FIRMWARE_TITLE = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE);
  88 + public static final String FIRMWARE_TAG = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TAG);
88 public static final String FIRMWARE_URL = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.URL); 89 public static final String FIRMWARE_URL = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.URL);
89 public static final String SOFTWARE_VERSION = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION); 90 public static final String SOFTWARE_VERSION = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION);
90 public static final String SOFTWARE_TITLE = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE); 91 public static final String SOFTWARE_TITLE = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE);
  92 + public static final String SOFTWARE_TAG = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TAG);
91 public static final String SOFTWARE_URL = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.URL); 93 public static final String SOFTWARE_URL = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.URL);
92 94
93 public static final String FIRMWARE_UPDATE_COAP_RESOURCE = "tbfw"; 95 public static final String FIRMWARE_UPDATE_COAP_RESOURCE = "tbfw";
@@ -165,6 +167,7 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl @@ -165,6 +167,7 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
165 if (fwInfo.isSupported()) { 167 if (fwInfo.isSupported()) {
166 attributesToFetch.add(FIRMWARE_TITLE); 168 attributesToFetch.add(FIRMWARE_TITLE);
167 attributesToFetch.add(FIRMWARE_VERSION); 169 attributesToFetch.add(FIRMWARE_VERSION);
  170 + attributesToFetch.add(FIRMWARE_TAG);
168 attributesToFetch.add(FIRMWARE_URL); 171 attributesToFetch.add(FIRMWARE_URL);
169 } 172 }
170 173
@@ -172,6 +175,7 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl @@ -172,6 +175,7 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
172 if (swInfo.isSupported()) { 175 if (swInfo.isSupported()) {
173 attributesToFetch.add(SOFTWARE_TITLE); 176 attributesToFetch.add(SOFTWARE_TITLE);
174 attributesToFetch.add(SOFTWARE_VERSION); 177 attributesToFetch.add(SOFTWARE_VERSION);
  178 + attributesToFetch.add(SOFTWARE_TAG);
175 attributesToFetch.add(SOFTWARE_URL); 179 attributesToFetch.add(SOFTWARE_URL);
176 } 180 }
177 181
@@ -186,17 +190,19 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl @@ -186,17 +190,19 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
186 if (fwInfo.isSupported()) { 190 if (fwInfo.isSupported()) {
187 Optional<String> newFwTitle = getAttributeValue(attrs, FIRMWARE_TITLE); 191 Optional<String> newFwTitle = getAttributeValue(attrs, FIRMWARE_TITLE);
188 Optional<String> newFwVersion = getAttributeValue(attrs, FIRMWARE_VERSION); 192 Optional<String> newFwVersion = getAttributeValue(attrs, FIRMWARE_VERSION);
  193 + Optional<String> newFwTag = getAttributeValue(attrs, FIRMWARE_TAG);
189 Optional<String> newFwUrl = getAttributeValue(attrs, FIRMWARE_URL); 194 Optional<String> newFwUrl = getAttributeValue(attrs, FIRMWARE_URL);
190 if (newFwTitle.isPresent() && newFwVersion.isPresent()) { 195 if (newFwTitle.isPresent() && newFwVersion.isPresent()) {
191 - onTargetFirmwareUpdate(client, newFwTitle.get(), newFwVersion.get(), newFwUrl); 196 + onTargetFirmwareUpdate(client, newFwTitle.get(), newFwVersion.get(), newFwUrl, newFwTag);
192 } 197 }
193 } 198 }
194 if (swInfo.isSupported()) { 199 if (swInfo.isSupported()) {
195 Optional<String> newSwTitle = getAttributeValue(attrs, SOFTWARE_TITLE); 200 Optional<String> newSwTitle = getAttributeValue(attrs, SOFTWARE_TITLE);
196 Optional<String> newSwVersion = getAttributeValue(attrs, SOFTWARE_VERSION); 201 Optional<String> newSwVersion = getAttributeValue(attrs, SOFTWARE_VERSION);
  202 + Optional<String> newSwTag = getAttributeValue(attrs, SOFTWARE_TAG);
197 Optional<String> newSwUrl = getAttributeValue(attrs, SOFTWARE_URL); 203 Optional<String> newSwUrl = getAttributeValue(attrs, SOFTWARE_URL);
198 if (newSwTitle.isPresent() && newSwVersion.isPresent()) { 204 if (newSwTitle.isPresent() && newSwVersion.isPresent()) {
199 - onTargetSoftwareUpdate(client, newSwTitle.get(), newSwVersion.get(), newSwUrl); 205 + onTargetSoftwareUpdate(client, newSwTitle.get(), newSwVersion.get(), newSwUrl, newSwTag);
200 } 206 }
201 } 207 }
202 }, throwable -> { 208 }, throwable -> {
@@ -216,9 +222,9 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl @@ -216,9 +222,9 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
216 } 222 }
217 223
218 @Override 224 @Override
219 - public void onTargetFirmwareUpdate(LwM2mClient client, String newFirmwareTitle, String newFirmwareVersion, Optional<String> newFirmwareUrl) { 225 + public void onTargetFirmwareUpdate(LwM2mClient client, String newFirmwareTitle, String newFirmwareVersion, Optional<String> newFirmwareUrl, Optional<String> newFirmwareTag) {
220 LwM2MClientFwOtaInfo fwInfo = getOrInitFwInfo(client); 226 LwM2MClientFwOtaInfo fwInfo = getOrInitFwInfo(client);
221 - fwInfo.updateTarget(newFirmwareTitle, newFirmwareVersion, newFirmwareUrl); 227 + fwInfo.updateTarget(newFirmwareTitle, newFirmwareVersion, newFirmwareUrl, newFirmwareTag);
222 update(fwInfo); 228 update(fwInfo);
223 startFirmwareUpdateIfNeeded(client, fwInfo); 229 startFirmwareUpdateIfNeeded(client, fwInfo);
224 } 230 }
@@ -354,9 +360,9 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl @@ -354,9 +360,9 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
354 } 360 }
355 361
356 @Override 362 @Override
357 - public void onTargetSoftwareUpdate(LwM2mClient client, String newSoftwareTitle, String newSoftwareVersion, Optional<String> newFirmwareUrl) { 363 + public void onTargetSoftwareUpdate(LwM2mClient client, String newSoftwareTitle, String newSoftwareVersion, Optional<String> newSoftwareUrl, Optional<String> newSoftwareTag) {
358 LwM2MClientSwOtaInfo fwInfo = getOrInitSwInfo(client); 364 LwM2MClientSwOtaInfo fwInfo = getOrInitSwInfo(client);
359 - fwInfo.updateTarget(newSoftwareTitle, newSoftwareVersion, newFirmwareUrl); 365 + fwInfo.updateTarget(newSoftwareTitle, newSoftwareVersion, newSoftwareUrl, newSoftwareTag);
360 update(fwInfo); 366 update(fwInfo);
361 startSoftwareUpdateIfNeeded(client, fwInfo); 367 startSoftwareUpdateIfNeeded(client, fwInfo);
362 } 368 }
@@ -368,7 +374,7 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl @@ -368,7 +374,7 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
368 sendStateUpdateToTelemetry(client, fwInfo, OtaPackageUpdateStatus.FAILED, "Client does not support firmware update or profile misconfiguration!"); 374 sendStateUpdateToTelemetry(client, fwInfo, OtaPackageUpdateStatus.FAILED, "Client does not support firmware update or profile misconfiguration!");
369 } else if (fwInfo.isUpdateRequired()) { 375 } else if (fwInfo.isUpdateRequired()) {
370 if (StringUtils.isNotEmpty(fwInfo.getTargetUrl())) { 376 if (StringUtils.isNotEmpty(fwInfo.getTargetUrl())) {
371 - log.debug("[{}] Starting update to [{}{}] using URL: {}", client.getEndpoint(), fwInfo.getTargetName(), fwInfo.getTargetVersion(), fwInfo.getTargetUrl()); 377 + log.debug("[{}] Starting update to [{}{}][] using URL: {}", client.getEndpoint(), fwInfo.getTargetName(), fwInfo.getTargetVersion(), fwInfo.getTargetUrl());
372 startUpdateUsingUrl(client, FW_URL_ID, fwInfo.getTargetUrl()); 378 startUpdateUsingUrl(client, FW_URL_ID, fwInfo.getTargetUrl());
373 } else { 379 } else {
374 log.debug("[{}] Starting update to [{}{}] using binary", client.getEndpoint(), fwInfo.getTargetName(), fwInfo.getTargetVersion()); 380 log.debug("[{}] Starting update to [{}{}] using binary", client.getEndpoint(), fwInfo.getTargetName(), fwInfo.getTargetVersion());
@@ -32,6 +32,7 @@ public abstract class LwM2MClientOtaInfo<Strategy, State, Result> { @@ -32,6 +32,7 @@ public abstract class LwM2MClientOtaInfo<Strategy, State, Result> {
32 32
33 protected String targetName; 33 protected String targetName;
34 protected String targetVersion; 34 protected String targetVersion;
  35 + protected String targetTag;
35 protected String targetUrl; 36 protected String targetUrl;
36 37
37 //TODO: use value from device if applicable; 38 //TODO: use value from device if applicable;
@@ -52,10 +53,11 @@ public abstract class LwM2MClientOtaInfo<Strategy, State, Result> { @@ -52,10 +53,11 @@ public abstract class LwM2MClientOtaInfo<Strategy, State, Result> {
52 this.strategy = strategy; 53 this.strategy = strategy;
53 } 54 }
54 55
55 - public void updateTarget(String targetName, String targetVersion, Optional<String> newTargetUrl) { 56 + public void updateTarget(String targetName, String targetVersion, Optional<String> newTargetUrl, Optional<String> newTargetTag) {
56 this.targetName = targetName; 57 this.targetName = targetName;
57 this.targetVersion = targetVersion; 58 this.targetVersion = targetVersion;
58 this.targetUrl = newTargetUrl.orElse(null); 59 this.targetUrl = newTargetUrl.orElse(null);
  60 + this.targetTag = newTargetTag.orElse(null);
59 } 61 }
60 62
61 @JsonIgnore 63 @JsonIgnore
@@ -64,13 +66,18 @@ public abstract class LwM2MClientOtaInfo<Strategy, State, Result> { @@ -64,13 +66,18 @@ public abstract class LwM2MClientOtaInfo<Strategy, State, Result> {
64 return false; 66 return false;
65 } else { 67 } else {
66 String targetPackageId = getPackageId(targetName, targetVersion); 68 String targetPackageId = getPackageId(targetName, targetVersion);
67 - String currentPackageIdUsingObject5 = getPackageId(currentName, currentVersion); 69 + String currentPackageId = getPackageId(currentName, currentVersion);
68 if (StringUtils.isNotEmpty(failedPackageId) && failedPackageId.equals(targetPackageId)) { 70 if (StringUtils.isNotEmpty(failedPackageId) && failedPackageId.equals(targetPackageId)) {
69 return false; 71 return false;
70 } else { 72 } else {
71 - if (targetPackageId.equals(currentPackageIdUsingObject5)) { 73 + if (targetPackageId.equals(currentPackageId)) {
  74 + return false;
  75 + } else if (StringUtils.isNotEmpty(targetTag) && targetTag.equals(currentPackageId)) {
72 return false; 76 return false;
73 } else if (StringUtils.isNotEmpty(currentVersion3)) { 77 } else if (StringUtils.isNotEmpty(currentVersion3)) {
  78 + if (StringUtils.isNotEmpty(targetTag) && currentVersion3.contains(targetTag)) {
  79 + return false;
  80 + }
74 return !currentVersion3.contains(targetPackageId); 81 return !currentVersion3.contains(targetPackageId);
75 } else { 82 } else {
76 return true; 83 return true;
@@ -26,9 +26,9 @@ public interface LwM2MOtaUpdateService { @@ -26,9 +26,9 @@ public interface LwM2MOtaUpdateService {
26 26
27 void forceFirmwareUpdate(LwM2mClient client); 27 void forceFirmwareUpdate(LwM2mClient client);
28 28
29 - void onTargetFirmwareUpdate(LwM2mClient client, String newFwTitle, String newFwVersion, Optional<String> newFwUrl); 29 + void onTargetFirmwareUpdate(LwM2mClient client, String newFwTitle, String newFwVersion, Optional<String> newFwUrl, Optional<String> newFwTag);
30 30
31 - void onTargetSoftwareUpdate(LwM2mClient client, String newSwTitle, String newSwVersion, Optional<String> newSwUrl); 31 + void onTargetSoftwareUpdate(LwM2mClient client, String newSwTitle, String newSwVersion, Optional<String> newSwUrl, Optional<String> newSwTag);
32 32
33 void onCurrentFirmwareNameUpdate(LwM2mClient client, String name); 33 void onCurrentFirmwareNameUpdate(LwM2mClient client, String name);
34 34
@@ -499,6 +499,7 @@ public class ModelConstants { @@ -499,6 +499,7 @@ public class ModelConstants {
499 public static final String OTA_PACKAGE_TYPE_COLUMN = "type"; 499 public static final String OTA_PACKAGE_TYPE_COLUMN = "type";
500 public static final String OTA_PACKAGE_TILE_COLUMN = TITLE_PROPERTY; 500 public static final String OTA_PACKAGE_TILE_COLUMN = TITLE_PROPERTY;
501 public static final String OTA_PACKAGE_VERSION_COLUMN = "version"; 501 public static final String OTA_PACKAGE_VERSION_COLUMN = "version";
  502 + public static final String OTA_PACKAGE_TAG_COLUMN = "tag";
502 public static final String OTA_PACKAGE_URL_COLUMN = "url"; 503 public static final String OTA_PACKAGE_URL_COLUMN = "url";
503 public static final String OTA_PACKAGE_FILE_NAME_COLUMN = "file_name"; 504 public static final String OTA_PACKAGE_FILE_NAME_COLUMN = "file_name";
504 public static final String OTA_PACKAGE_CONTENT_TYPE_COLUMN = "content_type"; 505 public static final String OTA_PACKAGE_CONTENT_TYPE_COLUMN = "content_type";
@@ -48,6 +48,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_DATA_S @@ -48,6 +48,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_DATA_S
48 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_DEVICE_PROFILE_ID_COLUMN; 48 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_DEVICE_PROFILE_ID_COLUMN;
49 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_FILE_NAME_COLUMN; 49 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_FILE_NAME_COLUMN;
50 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TABLE_NAME; 50 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TABLE_NAME;
  51 +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TAG_COLUMN;
51 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TENANT_ID_COLUMN; 52 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TENANT_ID_COLUMN;
52 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TILE_COLUMN; 53 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TILE_COLUMN;
53 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TYPE_COLUMN; 54 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TYPE_COLUMN;
@@ -78,6 +79,9 @@ public class OtaPackageEntity extends BaseSqlEntity<OtaPackage> implements Searc @@ -78,6 +79,9 @@ public class OtaPackageEntity extends BaseSqlEntity<OtaPackage> implements Searc
78 @Column(name = OTA_PACKAGE_VERSION_COLUMN) 79 @Column(name = OTA_PACKAGE_VERSION_COLUMN)
79 private String version; 80 private String version;
80 81
  82 + @Column(name = OTA_PACKAGE_TAG_COLUMN)
  83 + private String tag;
  84 +
81 @Column(name = OTA_PACKAGE_URL_COLUMN) 85 @Column(name = OTA_PACKAGE_URL_COLUMN)
82 private String url; 86 private String url;
83 87
@@ -112,24 +116,25 @@ public class OtaPackageEntity extends BaseSqlEntity<OtaPackage> implements Searc @@ -112,24 +116,25 @@ public class OtaPackageEntity extends BaseSqlEntity<OtaPackage> implements Searc
112 super(); 116 super();
113 } 117 }
114 118
115 - public OtaPackageEntity(OtaPackage firmware) {  
116 - this.createdTime = firmware.getCreatedTime();  
117 - this.setUuid(firmware.getUuidId());  
118 - this.tenantId = firmware.getTenantId().getId();  
119 - if (firmware.getDeviceProfileId() != null) {  
120 - this.deviceProfileId = firmware.getDeviceProfileId().getId(); 119 + public OtaPackageEntity(OtaPackage otaPackage) {
  120 + this.createdTime = otaPackage.getCreatedTime();
  121 + this.setUuid(otaPackage.getUuidId());
  122 + this.tenantId = otaPackage.getTenantId().getId();
  123 + if (otaPackage.getDeviceProfileId() != null) {
  124 + this.deviceProfileId = otaPackage.getDeviceProfileId().getId();
121 } 125 }
122 - this.type = firmware.getType();  
123 - this.title = firmware.getTitle();  
124 - this.version = firmware.getVersion();  
125 - this.url = firmware.getUrl();  
126 - this.fileName = firmware.getFileName();  
127 - this.contentType = firmware.getContentType();  
128 - this.checksumAlgorithm = firmware.getChecksumAlgorithm();  
129 - this.checksum = firmware.getChecksum();  
130 - this.data = firmware.getData().array();  
131 - this.dataSize = firmware.getDataSize();  
132 - this.additionalInfo = firmware.getAdditionalInfo(); 126 + this.type = otaPackage.getType();
  127 + this.title = otaPackage.getTitle();
  128 + this.version = otaPackage.getVersion();
  129 + this.tag = otaPackage.getTag();
  130 + this.url = otaPackage.getUrl();
  131 + this.fileName = otaPackage.getFileName();
  132 + this.contentType = otaPackage.getContentType();
  133 + this.checksumAlgorithm = otaPackage.getChecksumAlgorithm();
  134 + this.checksum = otaPackage.getChecksum();
  135 + this.data = otaPackage.getData().array();
  136 + this.dataSize = otaPackage.getDataSize();
  137 + this.additionalInfo = otaPackage.getAdditionalInfo();
133 } 138 }
134 139
135 @Override 140 @Override
@@ -144,26 +149,27 @@ public class OtaPackageEntity extends BaseSqlEntity<OtaPackage> implements Searc @@ -144,26 +149,27 @@ public class OtaPackageEntity extends BaseSqlEntity<OtaPackage> implements Searc
144 149
145 @Override 150 @Override
146 public OtaPackage toData() { 151 public OtaPackage toData() {
147 - OtaPackage firmware = new OtaPackage(new OtaPackageId(id));  
148 - firmware.setCreatedTime(createdTime);  
149 - firmware.setTenantId(new TenantId(tenantId)); 152 + OtaPackage otaPackage = new OtaPackage(new OtaPackageId(id));
  153 + otaPackage.setCreatedTime(createdTime);
  154 + otaPackage.setTenantId(new TenantId(tenantId));
150 if (deviceProfileId != null) { 155 if (deviceProfileId != null) {
151 - firmware.setDeviceProfileId(new DeviceProfileId(deviceProfileId)); 156 + otaPackage.setDeviceProfileId(new DeviceProfileId(deviceProfileId));
152 } 157 }
153 - firmware.setType(type);  
154 - firmware.setTitle(title);  
155 - firmware.setVersion(version);  
156 - firmware.setUrl(url);  
157 - firmware.setFileName(fileName);  
158 - firmware.setContentType(contentType);  
159 - firmware.setChecksumAlgorithm(checksumAlgorithm);  
160 - firmware.setChecksum(checksum);  
161 - firmware.setDataSize(dataSize); 158 + otaPackage.setType(type);
  159 + otaPackage.setTitle(title);
  160 + otaPackage.setVersion(version);
  161 + otaPackage.setTag(tag);
  162 + otaPackage.setUrl(url);
  163 + otaPackage.setFileName(fileName);
  164 + otaPackage.setContentType(contentType);
  165 + otaPackage.setChecksumAlgorithm(checksumAlgorithm);
  166 + otaPackage.setChecksum(checksum);
  167 + otaPackage.setDataSize(dataSize);
162 if (data != null) { 168 if (data != null) {
163 - firmware.setData(ByteBuffer.wrap(data));  
164 - firmware.setHasData(true); 169 + otaPackage.setData(ByteBuffer.wrap(data));
  170 + otaPackage.setHasData(true);
165 } 171 }
166 - firmware.setAdditionalInfo(additionalInfo);  
167 - return firmware; 172 + otaPackage.setAdditionalInfo(additionalInfo);
  173 + return otaPackage;
168 } 174 }
169 } 175 }
@@ -48,6 +48,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_DATA_S @@ -48,6 +48,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_DATA_S
48 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_DEVICE_PROFILE_ID_COLUMN; 48 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_DEVICE_PROFILE_ID_COLUMN;
49 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_FILE_NAME_COLUMN; 49 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_FILE_NAME_COLUMN;
50 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TABLE_NAME; 50 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TABLE_NAME;
  51 +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TAG_COLUMN;
51 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TENANT_ID_COLUMN; 52 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TENANT_ID_COLUMN;
52 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TILE_COLUMN; 53 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TILE_COLUMN;
53 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TYPE_COLUMN; 54 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TYPE_COLUMN;
@@ -78,6 +79,9 @@ public class OtaPackageInfoEntity extends BaseSqlEntity<OtaPackageInfo> implemen @@ -78,6 +79,9 @@ public class OtaPackageInfoEntity extends BaseSqlEntity<OtaPackageInfo> implemen
78 @Column(name = OTA_PACKAGE_VERSION_COLUMN) 79 @Column(name = OTA_PACKAGE_VERSION_COLUMN)
79 private String version; 80 private String version;
80 81
  82 + @Column(name = OTA_PACKAGE_TAG_COLUMN)
  83 + private String tag;
  84 +
81 @Column(name = OTA_PACKAGE_URL_COLUMN) 85 @Column(name = OTA_PACKAGE_URL_COLUMN)
82 private String url; 86 private String url;
83 87
@@ -111,26 +115,27 @@ public class OtaPackageInfoEntity extends BaseSqlEntity<OtaPackageInfo> implemen @@ -111,26 +115,27 @@ public class OtaPackageInfoEntity extends BaseSqlEntity<OtaPackageInfo> implemen
111 super(); 115 super();
112 } 116 }
113 117
114 - public OtaPackageInfoEntity(OtaPackageInfo firmware) {  
115 - this.createdTime = firmware.getCreatedTime();  
116 - this.setUuid(firmware.getUuidId());  
117 - this.tenantId = firmware.getTenantId().getId();  
118 - this.type = firmware.getType();  
119 - if (firmware.getDeviceProfileId() != null) {  
120 - this.deviceProfileId = firmware.getDeviceProfileId().getId(); 118 + public OtaPackageInfoEntity(OtaPackageInfo otaPackageInfo) {
  119 + this.createdTime = otaPackageInfo.getCreatedTime();
  120 + this.setUuid(otaPackageInfo.getUuidId());
  121 + this.tenantId = otaPackageInfo.getTenantId().getId();
  122 + this.type = otaPackageInfo.getType();
  123 + if (otaPackageInfo.getDeviceProfileId() != null) {
  124 + this.deviceProfileId = otaPackageInfo.getDeviceProfileId().getId();
121 } 125 }
122 - this.title = firmware.getTitle();  
123 - this.version = firmware.getVersion();  
124 - this.url = firmware.getUrl();  
125 - this.fileName = firmware.getFileName();  
126 - this.contentType = firmware.getContentType();  
127 - this.checksumAlgorithm = firmware.getChecksumAlgorithm();  
128 - this.checksum = firmware.getChecksum();  
129 - this.dataSize = firmware.getDataSize();  
130 - this.additionalInfo = firmware.getAdditionalInfo(); 126 + this.title = otaPackageInfo.getTitle();
  127 + this.version = otaPackageInfo.getVersion();
  128 + this.tag = otaPackageInfo.getTag();
  129 + this.url = otaPackageInfo.getUrl();
  130 + this.fileName = otaPackageInfo.getFileName();
  131 + this.contentType = otaPackageInfo.getContentType();
  132 + this.checksumAlgorithm = otaPackageInfo.getChecksumAlgorithm();
  133 + this.checksum = otaPackageInfo.getChecksum();
  134 + this.dataSize = otaPackageInfo.getDataSize();
  135 + this.additionalInfo = otaPackageInfo.getAdditionalInfo();
131 } 136 }
132 137
133 - public OtaPackageInfoEntity(UUID id, long createdTime, UUID tenantId, UUID deviceProfileId, OtaPackageType type, String title, String version, 138 + public OtaPackageInfoEntity(UUID id, long createdTime, UUID tenantId, UUID deviceProfileId, OtaPackageType type, String title, String version, String tag,
134 String url, String fileName, String contentType, ChecksumAlgorithm checksumAlgorithm, String checksum, Long dataSize, 139 String url, String fileName, String contentType, ChecksumAlgorithm checksumAlgorithm, String checksum, Long dataSize,
135 Object additionalInfo, boolean hasData) { 140 Object additionalInfo, boolean hasData) {
136 this.id = id; 141 this.id = id;
@@ -140,6 +145,7 @@ public class OtaPackageInfoEntity extends BaseSqlEntity<OtaPackageInfo> implemen @@ -140,6 +145,7 @@ public class OtaPackageInfoEntity extends BaseSqlEntity<OtaPackageInfo> implemen
140 this.type = type; 145 this.type = type;
141 this.title = title; 146 this.title = title;
142 this.version = version; 147 this.version = version;
  148 + this.tag = tag;
143 this.url = url; 149 this.url = url;
144 this.fileName = fileName; 150 this.fileName = fileName;
145 this.contentType = contentType; 151 this.contentType = contentType;
@@ -162,23 +168,24 @@ public class OtaPackageInfoEntity extends BaseSqlEntity<OtaPackageInfo> implemen @@ -162,23 +168,24 @@ public class OtaPackageInfoEntity extends BaseSqlEntity<OtaPackageInfo> implemen
162 168
163 @Override 169 @Override
164 public OtaPackageInfo toData() { 170 public OtaPackageInfo toData() {
165 - OtaPackageInfo firmware = new OtaPackageInfo(new OtaPackageId(id));  
166 - firmware.setCreatedTime(createdTime);  
167 - firmware.setTenantId(new TenantId(tenantId)); 171 + OtaPackageInfo otaPackageInfo = new OtaPackageInfo(new OtaPackageId(id));
  172 + otaPackageInfo.setCreatedTime(createdTime);
  173 + otaPackageInfo.setTenantId(new TenantId(tenantId));
168 if (deviceProfileId != null) { 174 if (deviceProfileId != null) {
169 - firmware.setDeviceProfileId(new DeviceProfileId(deviceProfileId)); 175 + otaPackageInfo.setDeviceProfileId(new DeviceProfileId(deviceProfileId));
170 } 176 }
171 - firmware.setType(type);  
172 - firmware.setTitle(title);  
173 - firmware.setVersion(version);  
174 - firmware.setUrl(url);  
175 - firmware.setFileName(fileName);  
176 - firmware.setContentType(contentType);  
177 - firmware.setChecksumAlgorithm(checksumAlgorithm);  
178 - firmware.setChecksum(checksum);  
179 - firmware.setDataSize(dataSize);  
180 - firmware.setAdditionalInfo(additionalInfo);  
181 - firmware.setHasData(hasData);  
182 - return firmware; 177 + otaPackageInfo.setType(type);
  178 + otaPackageInfo.setTitle(title);
  179 + otaPackageInfo.setVersion(version);
  180 + otaPackageInfo.setTag(tag);
  181 + otaPackageInfo.setUrl(url);
  182 + otaPackageInfo.setFileName(fileName);
  183 + otaPackageInfo.setContentType(contentType);
  184 + otaPackageInfo.setChecksumAlgorithm(checksumAlgorithm);
  185 + otaPackageInfo.setChecksum(checksum);
  186 + otaPackageInfo.setDataSize(dataSize);
  187 + otaPackageInfo.setAdditionalInfo(additionalInfo);
  188 + otaPackageInfo.setHasData(hasData);
  189 + return otaPackageInfo;
183 } 190 }
184 } 191 }
@@ -51,6 +51,7 @@ import org.thingsboard.server.dao.tenant.TenantDao; @@ -51,6 +51,7 @@ import org.thingsboard.server.dao.tenant.TenantDao;
51 import java.nio.ByteBuffer; 51 import java.nio.ByteBuffer;
52 import java.util.Collections; 52 import java.util.Collections;
53 import java.util.List; 53 import java.util.List;
  54 +import java.util.Objects;
54 import java.util.Optional; 55 import java.util.Optional;
55 56
56 import static org.thingsboard.server.common.data.CacheConstants.OTA_PACKAGE_CACHE; 57 import static org.thingsboard.server.common.data.CacheConstants.OTA_PACKAGE_CACHE;
@@ -318,6 +319,10 @@ public class BaseOtaPackageService implements OtaPackageService { @@ -318,6 +319,10 @@ public class BaseOtaPackageService implements OtaPackageService {
318 throw new DataValidationException("Updating otaPackage version is prohibited!"); 319 throw new DataValidationException("Updating otaPackage version is prohibited!");
319 } 320 }
320 321
  322 + if (!Objects.equals(otaPackage.getTag(), otaPackageOld.getTag())) {
  323 + throw new DataValidationException("Updating otaPackage tag is prohibited!");
  324 + }
  325 +
321 if (!otaPackageOld.getDeviceProfileId().equals(otaPackage.getDeviceProfileId())) { 326 if (!otaPackageOld.getDeviceProfileId().equals(otaPackage.getDeviceProfileId())) {
322 throw new DataValidationException("Updating otaPackage deviceProfile is prohibited!"); 327 throw new DataValidationException("Updating otaPackage deviceProfile is prohibited!");
323 } 328 }
@@ -26,14 +26,14 @@ import org.thingsboard.server.dao.model.sql.OtaPackageInfoEntity; @@ -26,14 +26,14 @@ import org.thingsboard.server.dao.model.sql.OtaPackageInfoEntity;
26 import java.util.UUID; 26 import java.util.UUID;
27 27
28 public interface OtaPackageInfoRepository extends CrudRepository<OtaPackageInfoEntity, UUID> { 28 public interface OtaPackageInfoRepository extends CrudRepository<OtaPackageInfoEntity, UUID> {
29 - @Query("SELECT new OtaPackageInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.url, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, CASE WHEN (f.data IS NOT NULL OR f.url IS NOT NULL) THEN true ELSE false END) FROM OtaPackageEntity f WHERE " + 29 + @Query("SELECT new OtaPackageInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.tag, f.url, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, CASE WHEN (f.data IS NOT NULL OR f.url IS NOT NULL) THEN true ELSE false END) FROM OtaPackageEntity f WHERE " +
30 "f.tenantId = :tenantId " + 30 "f.tenantId = :tenantId " +
31 "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") 31 "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
32 Page<OtaPackageInfoEntity> findAllByTenantId(@Param("tenantId") UUID tenantId, 32 Page<OtaPackageInfoEntity> findAllByTenantId(@Param("tenantId") UUID tenantId,
33 @Param("searchText") String searchText, 33 @Param("searchText") String searchText,
34 Pageable pageable); 34 Pageable pageable);
35 35
36 - @Query("SELECT new OtaPackageInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.url, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, true) FROM OtaPackageEntity f WHERE " + 36 + @Query("SELECT new OtaPackageInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.tag, f.url, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, true) FROM OtaPackageEntity f WHERE " +
37 "f.tenantId = :tenantId " + 37 "f.tenantId = :tenantId " +
38 "AND f.deviceProfileId = :deviceProfileId " + 38 "AND f.deviceProfileId = :deviceProfileId " +
39 "AND f.type = :type " + 39 "AND f.type = :type " +
@@ -45,7 +45,7 @@ public interface OtaPackageInfoRepository extends CrudRepository<OtaPackageInfoE @@ -45,7 +45,7 @@ public interface OtaPackageInfoRepository extends CrudRepository<OtaPackageInfoE
45 @Param("searchText") String searchText, 45 @Param("searchText") String searchText,
46 Pageable pageable); 46 Pageable pageable);
47 47
48 - @Query("SELECT new OtaPackageInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.url, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, CASE WHEN (f.data IS NOT NULL OR f.url IS NOT NULL) THEN true ELSE false END) FROM OtaPackageEntity f WHERE f.id = :id") 48 + @Query("SELECT new OtaPackageInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.tag, f.url, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, CASE WHEN (f.data IS NOT NULL OR f.url IS NOT NULL) THEN true ELSE false END) FROM OtaPackageEntity f WHERE f.id = :id")
49 OtaPackageInfoEntity findOtaPackageInfoById(@Param("id") UUID id); 49 OtaPackageInfoEntity findOtaPackageInfoById(@Param("id") UUID id);
50 50
51 @Query(value = "SELECT exists(SELECT * " + 51 @Query(value = "SELECT exists(SELECT * " +
@@ -173,6 +173,7 @@ CREATE TABLE IF NOT EXISTS ota_package ( @@ -173,6 +173,7 @@ CREATE TABLE IF NOT EXISTS ota_package (
173 type varchar(32) NOT NULL, 173 type varchar(32) NOT NULL,
174 title varchar(255) NOT NULL, 174 title varchar(255) NOT NULL,
175 version varchar(255) NOT NULL, 175 version varchar(255) NOT NULL,
  176 + tag varchar(255),
176 url varchar(255), 177 url varchar(255),
177 file_name varchar(255), 178 file_name varchar(255),
178 content_type varchar(255), 179 content_type varchar(255),
@@ -188,6 +188,7 @@ CREATE TABLE IF NOT EXISTS ota_package ( @@ -188,6 +188,7 @@ CREATE TABLE IF NOT EXISTS ota_package (
188 type varchar(32) NOT NULL, 188 type varchar(32) NOT NULL,
189 title varchar(255) NOT NULL, 189 title varchar(255) NOT NULL,
190 version varchar(255) NOT NULL, 190 version varchar(255) NOT NULL,
  191 + tag varchar(255),
191 url varchar(255), 192 url varchar(255),
192 file_name varchar(255), 193 file_name varchar(255),
193 content_type varchar(255), 194 content_type varchar(255),
@@ -59,9 +59,10 @@ export class OtaUpdateTableConfigResolve implements Resolve<EntityTableConfig<Ot @@ -59,9 +59,10 @@ export class OtaUpdateTableConfigResolve implements Resolve<EntityTableConfig<Ot
59 59
60 this.config.columns.push( 60 this.config.columns.push(
61 new DateEntityTableColumn<OtaPackageInfo>('createdTime', 'common.created-time', this.datePipe, '150px'), 61 new DateEntityTableColumn<OtaPackageInfo>('createdTime', 'common.created-time', this.datePipe, '150px'),
62 - new EntityTableColumn<OtaPackageInfo>('title', 'ota-update.title', '20%'),  
63 - new EntityTableColumn<OtaPackageInfo>('version', 'ota-update.version', '20%'),  
64 - new EntityTableColumn<OtaPackageInfo>('type', 'ota-update.package-type', '20%', entity => { 62 + new EntityTableColumn<OtaPackageInfo>('title', 'ota-update.title', '15%'),
  63 + new EntityTableColumn<OtaPackageInfo>('version', 'ota-update.version', '15%'),
  64 + new EntityTableColumn<OtaPackageInfo>('tag', 'ota-update.version-tag', '15%'),
  65 + new EntityTableColumn<OtaPackageInfo>('type', 'ota-update.package-type', '15%', entity => {
65 return this.translate.instant(OtaUpdateTypeTranslationMap.get(entity.type)); 66 return this.translate.instant(OtaUpdateTypeTranslationMap.get(entity.type));
66 }), 67 }),
67 new EntityTableColumn<OtaPackageInfo>('url', 'ota-update.direct-url', '20%', entity => { 68 new EntityTableColumn<OtaPackageInfo>('url', 'ota-update.direct-url', '20%', entity => {
@@ -74,6 +74,11 @@ @@ -74,6 +74,11 @@
74 </mat-error> 74 </mat-error>
75 </mat-form-field> 75 </mat-form-field>
76 </div> 76 </div>
  77 + <mat-form-field class="mat-block" fxFlex style="margin-bottom: 8px">
  78 + <mat-label translate>ota-update.version-tag</mat-label>
  79 + <input matInput formControlName="tag" type="text" [readonly]="!isAdd">
  80 + <mat-hint *ngIf="isAdd" translate>ota-update.version-tag-hint</mat-hint>
  81 + </mat-form-field>
77 <tb-device-profile-autocomplete 82 <tb-device-profile-autocomplete
78 formControlName="deviceProfileId" 83 formControlName="deviceProfileId"
79 required 84 required
@@ -94,8 +99,8 @@ @@ -94,8 +99,8 @@
94 <section *ngIf="isAdd"> 99 <section *ngIf="isAdd">
95 <div class="mat-caption" style="margin: -8px 0 8px;" translate>ota-update.warning-after-save-no-edit</div> 100 <div class="mat-caption" style="margin: -8px 0 8px;" translate>ota-update.warning-after-save-no-edit</div>
96 <mat-radio-group formControlName="isURL" fxLayoutGap="16px"> 101 <mat-radio-group formControlName="isURL" fxLayoutGap="16px">
97 - <mat-radio-button [value]="false">Upload binary file</mat-radio-button>  
98 - <mat-radio-button [value]="true">Use external URL</mat-radio-button> 102 + <mat-radio-button [value]="false">{{ "ota-update.upload-binary-file" | translate }}</mat-radio-button>
  103 + <mat-radio-button [value]="true">{{ "ota-update.use-external-url" | translate }}</mat-radio-button>
99 </mat-radio-group> 104 </mat-radio-group>
100 </section> 105 </section>
101 <section *ngIf="!entityForm.get('isURL').value"> 106 <section *ngIf="!entityForm.get('isURL').value">
@@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
15 /// 15 ///
16 16
17 import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; 17 import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
18 -import { Subject } from 'rxjs'; 18 +import { combineLatest, Subject } from 'rxjs';
19 import { Store } from '@ngrx/store'; 19 import { Store } from '@ngrx/store';
20 import { AppState } from '@core/core.state'; 20 import { AppState } from '@core/core.state';
21 import { TranslateService } from '@ngx-translate/core'; 21 import { TranslateService } from '@ngx-translate/core';
@@ -30,7 +30,7 @@ import { @@ -30,7 +30,7 @@ import {
30 OtaUpdateTypeTranslationMap 30 OtaUpdateTypeTranslationMap
31 } from '@shared/models/ota-package.models'; 31 } from '@shared/models/ota-package.models';
32 import { ActionNotificationShow } from '@core/notification/notification.actions'; 32 import { ActionNotificationShow } from '@core/notification/notification.actions';
33 -import { filter, takeUntil } from 'rxjs/operators'; 33 +import { filter, startWith, takeUntil } from 'rxjs/operators';
34 import { isNotEmptyStr } from '@core/utils'; 34 import { isNotEmptyStr } from '@core/utils';
35 35
36 @Component({ 36 @Component({
@@ -56,22 +56,33 @@ export class OtaUpdateComponent extends EntityComponent<OtaPackage> implements O @@ -56,22 +56,33 @@ export class OtaUpdateComponent extends EntityComponent<OtaPackage> implements O
56 56
57 ngOnInit() { 57 ngOnInit() {
58 super.ngOnInit(); 58 super.ngOnInit();
59 - this.entityForm.get('isURL').valueChanges.pipe(  
60 - filter(() => this.isAdd),  
61 - takeUntil(this.destroy$)  
62 - ).subscribe((isURL) => {  
63 - if (isURL === false) {  
64 - this.entityForm.get('url').clearValidators();  
65 - this.entityForm.get('file').setValidators(Validators.required);  
66 - this.entityForm.get('url').updateValueAndValidity({emitEvent: false});  
67 - this.entityForm.get('file').updateValueAndValidity({emitEvent: false});  
68 - } else {  
69 - this.entityForm.get('file').clearValidators();  
70 - this.entityForm.get('url').setValidators([Validators.required, Validators.pattern('(.|\\s)*\\S(.|\\s)*')]);  
71 - this.entityForm.get('file').updateValueAndValidity({emitEvent: false});  
72 - this.entityForm.get('url').updateValueAndValidity({emitEvent: false});  
73 - }  
74 - }); 59 + if (this.isAdd) {
  60 + this.entityForm.get('isURL').valueChanges.pipe(
  61 + takeUntil(this.destroy$)
  62 + ).subscribe((isURL) => {
  63 + if (isURL === false) {
  64 + this.entityForm.get('url').clearValidators();
  65 + this.entityForm.get('file').setValidators(Validators.required);
  66 + this.entityForm.get('url').updateValueAndValidity({emitEvent: false});
  67 + this.entityForm.get('file').updateValueAndValidity({emitEvent: false});
  68 + } else {
  69 + this.entityForm.get('file').clearValidators();
  70 + this.entityForm.get('url').setValidators([Validators.required, Validators.pattern('(.|\\s)*\\S(.|\\s)*')]);
  71 + this.entityForm.get('file').updateValueAndValidity({emitEvent: false});
  72 + this.entityForm.get('url').updateValueAndValidity({emitEvent: false});
  73 + }
  74 + });
  75 + combineLatest([
  76 + this.entityForm.get('title').valueChanges.pipe(startWith('')),
  77 + this.entityForm.get('version').valueChanges.pipe(startWith(''))
  78 + ]).pipe(
  79 + filter(() => this.entityForm.get('tag').pristine),
  80 + takeUntil(this.destroy$)
  81 + ).subscribe(([title, version]) => {
  82 + const tag = (`${title} ${version}`).trim();
  83 + this.entityForm.get('tag').patchValue(tag);
  84 + });
  85 + }
75 } 86 }
76 87
77 ngOnDestroy() { 88 ngOnDestroy() {
@@ -92,6 +103,7 @@ export class OtaUpdateComponent extends EntityComponent<OtaPackage> implements O @@ -92,6 +103,7 @@ export class OtaUpdateComponent extends EntityComponent<OtaPackage> implements O
92 const form = this.fb.group({ 103 const form = this.fb.group({
93 title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]], 104 title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]],
94 version: [entity ? entity.version : '', [Validators.required, Validators.maxLength(255)]], 105 version: [entity ? entity.version : '', [Validators.required, Validators.maxLength(255)]],
  106 + tag: [entity ? entity.tag : '', [Validators.maxLength(255)]],
95 type: [entity?.type ? entity.type : OtaUpdateType.FIRMWARE, Validators.required], 107 type: [entity?.type ? entity.type : OtaUpdateType.FIRMWARE, Validators.required],
96 deviceProfileId: [entity ? entity.deviceProfileId : null, Validators.required], 108 deviceProfileId: [entity ? entity.deviceProfileId : null, Validators.required],
97 checksumAlgorithm: [entity && entity.checksumAlgorithm ? entity.checksumAlgorithm : ChecksumAlgorithm.SHA256], 109 checksumAlgorithm: [entity && entity.checksumAlgorithm ? entity.checksumAlgorithm : ChecksumAlgorithm.SHA256],
@@ -119,6 +131,7 @@ export class OtaUpdateComponent extends EntityComponent<OtaPackage> implements O @@ -119,6 +131,7 @@ export class OtaUpdateComponent extends EntityComponent<OtaPackage> implements O
119 this.entityForm.patchValue({ 131 this.entityForm.patchValue({
120 title: entity.title, 132 title: entity.title,
121 version: entity.version, 133 version: entity.version,
  134 + tag: entity.tag,
122 type: entity.type, 135 type: entity.type,
123 deviceProfileId: entity.deviceProfileId, 136 deviceProfileId: entity.deviceProfileId,
124 checksumAlgorithm: entity.checksumAlgorithm, 137 checksumAlgorithm: entity.checksumAlgorithm,
@@ -91,6 +91,7 @@ export interface OtaPackageInfo extends BaseData<OtaPackageId> { @@ -91,6 +91,7 @@ export interface OtaPackageInfo extends BaseData<OtaPackageId> {
91 deviceProfileId?: DeviceProfileId; 91 deviceProfileId?: DeviceProfileId;
92 title?: string; 92 title?: string;
93 version?: string; 93 version?: string;
  94 + tag?: string;
94 hasData?: boolean; 95 hasData?: boolean;
95 url?: string; 96 url?: string;
96 fileName: string; 97 fileName: string;
@@ -2341,8 +2341,12 @@ @@ -2341,8 +2341,12 @@
2341 "firmware": "Firmware", 2341 "firmware": "Firmware",
2342 "software": "Software" 2342 "software": "Software"
2343 }, 2343 },
  2344 + "upload-binary-file": "Upload binary file",
  2345 + "use-external-url": "Use external URL",
2344 "version": "Version", 2346 "version": "Version",
2345 "version-required": "Version is required.", 2347 "version-required": "Version is required.",
  2348 + "version-tag": "Version Tag",
  2349 + "version-tag-hint": "Custom tag should match the package version reported by your device.",
2346 "warning-after-save-no-edit": "Once the package is uploaded, you will not be able to modify title, version, device profile and package type." 2350 "warning-after-save-no-edit": "Once the package is uploaded, you will not be able to modify title, version, device profile and package type."
2347 }, 2351 },
2348 "position": { 2352 "position": {