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 67 type varchar(32) NOT NULL,
68 68 title varchar(255) NOT NULL,
69 69 version varchar(255) NOT NULL,
  70 + tag varchar(255),
70 71 url varchar(255),
71 72 file_name varchar(255),
72 73 content_type varchar(255),
... ...
... ... @@ -146,6 +146,7 @@ public class OtaPackageController extends BaseController {
146 146 otaPackage.setType(info.getType());
147 147 otaPackage.setTitle(info.getTitle());
148 148 otaPackage.setVersion(info.getVersion());
  149 + otaPackage.setTag(info.getTag());
149 150 otaPackage.setAdditionalInfo(info.getAdditionalInfo());
150 151
151 152 ChecksumAlgorithm checksumAlgorithm = ChecksumAlgorithm.valueOf(checksumAlgorithmStr.toUpperCase());
... ...
... ... @@ -64,6 +64,7 @@ import static org.thingsboard.server.common.data.ota.OtaPackageKey.CHECKSUM;
64 64 import static org.thingsboard.server.common.data.ota.OtaPackageKey.CHECKSUM_ALGORITHM;
65 65 import static org.thingsboard.server.common.data.ota.OtaPackageKey.SIZE;
66 66 import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE;
  67 +import static org.thingsboard.server.common.data.ota.OtaPackageKey.TAG;
67 68 import static org.thingsboard.server.common.data.ota.OtaPackageKey.TITLE;
68 69 import static org.thingsboard.server.common.data.ota.OtaPackageKey.TS;
69 70 import static org.thingsboard.server.common.data.ota.OtaPackageKey.URL;
... ... @@ -246,6 +247,11 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService {
246 247 List<TsKvEntry> telemetry = new ArrayList<>();
247 248 telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), TITLE), firmware.getTitle())));
248 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 255 telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(getTargetTelemetryKey(firmware.getType(), TS), ts)));
250 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 295 List<AttributeKvEntry> attributes = new ArrayList<>();
290 296 attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, TITLE), otaPackage.getTitle())));
291 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 301 if (otaPackage.hasUrl()) {
293 302 attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, URL), otaPackage.getUrl())));
294 303 List<String> attrToRemove = new ArrayList<>();
... ...
... ... @@ -37,6 +37,7 @@ public class OtaPackageInfo extends SearchTextBasedWithAdditionalInfo<OtaPackage
37 37 private OtaPackageType type;
38 38 private String title;
39 39 private String version;
  40 + private String tag;
40 41 private String url;
41 42 private boolean hasData;
42 43 private String fileName;
... ... @@ -61,6 +62,7 @@ public class OtaPackageInfo extends SearchTextBasedWithAdditionalInfo<OtaPackage
61 62 this.type = otaPackageInfo.getType();
62 63 this.title = otaPackageInfo.getTitle();
63 64 this.version = otaPackageInfo.getVersion();
  65 + this.tag = otaPackageInfo.getTag();
64 66 this.url = otaPackageInfo.getUrl();
65 67 this.hasData = otaPackageInfo.isHasData();
66 68 this.fileName = otaPackageInfo.getFileName();
... ...
... ... @@ -19,7 +19,7 @@ import lombok.Getter;
19 19
20 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 24 @Getter
25 25 private final String value;
... ...
... ... @@ -120,9 +120,11 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService {
120 120 if (msg.getSharedUpdatedCount() > 0 && lwM2MClient != null) {
121 121 String newFirmwareTitle = null;
122 122 String newFirmwareVersion = null;
  123 + String newFirmwareTag = null;
123 124 String newFirmwareUrl = null;
124 125 String newSoftwareTitle = null;
125 126 String newSoftwareVersion = null;
  127 + String newSoftwareTag = null;
126 128 String newSoftwareUrl = null;
127 129 List<TransportProtos.TsKvProto> otherAttributes = new ArrayList<>();
128 130 for (TransportProtos.TsKvProto tsKvProto : msg.getSharedUpdatedList()) {
... ... @@ -131,12 +133,16 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService {
131 133 newFirmwareTitle = getStrValue(tsKvProto);
132 134 } else if (DefaultLwM2MOtaUpdateService.FIRMWARE_VERSION.equals(attrName)) {
133 135 newFirmwareVersion = getStrValue(tsKvProto);
  136 + } else if (DefaultLwM2MOtaUpdateService.FIRMWARE_TAG.equals(attrName)) {
  137 + newFirmwareTag = getStrValue(tsKvProto);
134 138 } else if (DefaultLwM2MOtaUpdateService.FIRMWARE_URL.equals(attrName)) {
135 139 newFirmwareUrl = getStrValue(tsKvProto);
136 140 } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_TITLE.equals(attrName)) {
137 141 newSoftwareTitle = getStrValue(tsKvProto);
138 142 } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_VERSION.equals(attrName)) {
139 143 newSoftwareVersion = getStrValue(tsKvProto);
  144 + } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_TAG.equals(attrName)) {
  145 + newSoftwareTag = getStrValue(tsKvProto);
140 146 } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_URL.equals(attrName)) {
141 147 newSoftwareUrl = getStrValue(tsKvProto);
142 148 }else {
... ... @@ -144,10 +150,10 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService {
144 150 }
145 151 }
146 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 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 158 if (!otherAttributes.isEmpty()) {
153 159 onAttributesUpdate(lwM2MClient, otherAttributes);
... ...
... ... @@ -85,9 +85,11 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
85 85
86 86 public static final String FIRMWARE_VERSION = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION);
87 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 89 public static final String FIRMWARE_URL = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.URL);
89 90 public static final String SOFTWARE_VERSION = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION);
90 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 93 public static final String SOFTWARE_URL = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.URL);
92 94
93 95 public static final String FIRMWARE_UPDATE_COAP_RESOURCE = "tbfw";
... ... @@ -165,6 +167,7 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
165 167 if (fwInfo.isSupported()) {
166 168 attributesToFetch.add(FIRMWARE_TITLE);
167 169 attributesToFetch.add(FIRMWARE_VERSION);
  170 + attributesToFetch.add(FIRMWARE_TAG);
168 171 attributesToFetch.add(FIRMWARE_URL);
169 172 }
170 173
... ... @@ -172,6 +175,7 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
172 175 if (swInfo.isSupported()) {
173 176 attributesToFetch.add(SOFTWARE_TITLE);
174 177 attributesToFetch.add(SOFTWARE_VERSION);
  178 + attributesToFetch.add(SOFTWARE_TAG);
175 179 attributesToFetch.add(SOFTWARE_URL);
176 180 }
177 181
... ... @@ -186,17 +190,19 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
186 190 if (fwInfo.isSupported()) {
187 191 Optional<String> newFwTitle = getAttributeValue(attrs, FIRMWARE_TITLE);
188 192 Optional<String> newFwVersion = getAttributeValue(attrs, FIRMWARE_VERSION);
  193 + Optional<String> newFwTag = getAttributeValue(attrs, FIRMWARE_TAG);
189 194 Optional<String> newFwUrl = getAttributeValue(attrs, FIRMWARE_URL);
190 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 199 if (swInfo.isSupported()) {
195 200 Optional<String> newSwTitle = getAttributeValue(attrs, SOFTWARE_TITLE);
196 201 Optional<String> newSwVersion = getAttributeValue(attrs, SOFTWARE_VERSION);
  202 + Optional<String> newSwTag = getAttributeValue(attrs, SOFTWARE_TAG);
197 203 Optional<String> newSwUrl = getAttributeValue(attrs, SOFTWARE_URL);
198 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 208 }, throwable -> {
... ... @@ -216,9 +222,9 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
216 222 }
217 223
218 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 226 LwM2MClientFwOtaInfo fwInfo = getOrInitFwInfo(client);
221   - fwInfo.updateTarget(newFirmwareTitle, newFirmwareVersion, newFirmwareUrl);
  227 + fwInfo.updateTarget(newFirmwareTitle, newFirmwareVersion, newFirmwareUrl, newFirmwareTag);
222 228 update(fwInfo);
223 229 startFirmwareUpdateIfNeeded(client, fwInfo);
224 230 }
... ... @@ -354,9 +360,9 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
354 360 }
355 361
356 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 364 LwM2MClientSwOtaInfo fwInfo = getOrInitSwInfo(client);
359   - fwInfo.updateTarget(newSoftwareTitle, newSoftwareVersion, newFirmwareUrl);
  365 + fwInfo.updateTarget(newSoftwareTitle, newSoftwareVersion, newSoftwareUrl, newSoftwareTag);
360 366 update(fwInfo);
361 367 startSoftwareUpdateIfNeeded(client, fwInfo);
362 368 }
... ... @@ -368,7 +374,7 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
368 374 sendStateUpdateToTelemetry(client, fwInfo, OtaPackageUpdateStatus.FAILED, "Client does not support firmware update or profile misconfiguration!");
369 375 } else if (fwInfo.isUpdateRequired()) {
370 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 378 startUpdateUsingUrl(client, FW_URL_ID, fwInfo.getTargetUrl());
373 379 } else {
374 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 32
33 33 protected String targetName;
34 34 protected String targetVersion;
  35 + protected String targetTag;
35 36 protected String targetUrl;
36 37
37 38 //TODO: use value from device if applicable;
... ... @@ -52,10 +53,11 @@ public abstract class LwM2MClientOtaInfo<Strategy, State, Result> {
52 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 57 this.targetName = targetName;
57 58 this.targetVersion = targetVersion;
58 59 this.targetUrl = newTargetUrl.orElse(null);
  60 + this.targetTag = newTargetTag.orElse(null);
59 61 }
60 62
61 63 @JsonIgnore
... ... @@ -64,13 +66,18 @@ public abstract class LwM2MClientOtaInfo<Strategy, State, Result> {
64 66 return false;
65 67 } else {
66 68 String targetPackageId = getPackageId(targetName, targetVersion);
67   - String currentPackageIdUsingObject5 = getPackageId(currentName, currentVersion);
  69 + String currentPackageId = getPackageId(currentName, currentVersion);
68 70 if (StringUtils.isNotEmpty(failedPackageId) && failedPackageId.equals(targetPackageId)) {
69 71 return false;
70 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 76 return false;
73 77 } else if (StringUtils.isNotEmpty(currentVersion3)) {
  78 + if (StringUtils.isNotEmpty(targetTag) && currentVersion3.contains(targetTag)) {
  79 + return false;
  80 + }
74 81 return !currentVersion3.contains(targetPackageId);
75 82 } else {
76 83 return true;
... ...
... ... @@ -26,9 +26,9 @@ public interface LwM2MOtaUpdateService {
26 26
27 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 33 void onCurrentFirmwareNameUpdate(LwM2mClient client, String name);
34 34
... ...
... ... @@ -499,6 +499,7 @@ public class ModelConstants {
499 499 public static final String OTA_PACKAGE_TYPE_COLUMN = "type";
500 500 public static final String OTA_PACKAGE_TILE_COLUMN = TITLE_PROPERTY;
501 501 public static final String OTA_PACKAGE_VERSION_COLUMN = "version";
  502 + public static final String OTA_PACKAGE_TAG_COLUMN = "tag";
502 503 public static final String OTA_PACKAGE_URL_COLUMN = "url";
503 504 public static final String OTA_PACKAGE_FILE_NAME_COLUMN = "file_name";
504 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 48 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_DEVICE_PROFILE_ID_COLUMN;
49 49 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_FILE_NAME_COLUMN;
50 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 52 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TENANT_ID_COLUMN;
52 53 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TILE_COLUMN;
53 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 79 @Column(name = OTA_PACKAGE_VERSION_COLUMN)
79 80 private String version;
80 81
  82 + @Column(name = OTA_PACKAGE_TAG_COLUMN)
  83 + private String tag;
  84 +
81 85 @Column(name = OTA_PACKAGE_URL_COLUMN)
82 86 private String url;
83 87
... ... @@ -112,24 +116,25 @@ public class OtaPackageEntity extends BaseSqlEntity<OtaPackage> implements Searc
112 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 140 @Override
... ... @@ -144,26 +149,27 @@ public class OtaPackageEntity extends BaseSqlEntity<OtaPackage> implements Searc
144 149
145 150 @Override
146 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 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 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 48 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_DEVICE_PROFILE_ID_COLUMN;
49 49 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_FILE_NAME_COLUMN;
50 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 52 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TENANT_ID_COLUMN;
52 53 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TILE_COLUMN;
53 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 79 @Column(name = OTA_PACKAGE_VERSION_COLUMN)
79 80 private String version;
80 81
  82 + @Column(name = OTA_PACKAGE_TAG_COLUMN)
  83 + private String tag;
  84 +
81 85 @Column(name = OTA_PACKAGE_URL_COLUMN)
82 86 private String url;
83 87
... ... @@ -111,26 +115,27 @@ public class OtaPackageInfoEntity extends BaseSqlEntity<OtaPackageInfo> implemen
111 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 139 String url, String fileName, String contentType, ChecksumAlgorithm checksumAlgorithm, String checksum, Long dataSize,
135 140 Object additionalInfo, boolean hasData) {
136 141 this.id = id;
... ... @@ -140,6 +145,7 @@ public class OtaPackageInfoEntity extends BaseSqlEntity<OtaPackageInfo> implemen
140 145 this.type = type;
141 146 this.title = title;
142 147 this.version = version;
  148 + this.tag = tag;
143 149 this.url = url;
144 150 this.fileName = fileName;
145 151 this.contentType = contentType;
... ... @@ -162,23 +168,24 @@ public class OtaPackageInfoEntity extends BaseSqlEntity<OtaPackageInfo> implemen
162 168
163 169 @Override
164 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 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 51 import java.nio.ByteBuffer;
52 52 import java.util.Collections;
53 53 import java.util.List;
  54 +import java.util.Objects;
54 55 import java.util.Optional;
55 56
56 57 import static org.thingsboard.server.common.data.CacheConstants.OTA_PACKAGE_CACHE;
... ... @@ -318,6 +319,10 @@ public class BaseOtaPackageService implements OtaPackageService {
318 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 326 if (!otaPackageOld.getDeviceProfileId().equals(otaPackage.getDeviceProfileId())) {
322 327 throw new DataValidationException("Updating otaPackage deviceProfile is prohibited!");
323 328 }
... ...
... ... @@ -26,14 +26,14 @@ import org.thingsboard.server.dao.model.sql.OtaPackageInfoEntity;
26 26 import java.util.UUID;
27 27
28 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 30 "f.tenantId = :tenantId " +
31 31 "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
32 32 Page<OtaPackageInfoEntity> findAllByTenantId(@Param("tenantId") UUID tenantId,
33 33 @Param("searchText") String searchText,
34 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 37 "f.tenantId = :tenantId " +
38 38 "AND f.deviceProfileId = :deviceProfileId " +
39 39 "AND f.type = :type " +
... ... @@ -45,7 +45,7 @@ public interface OtaPackageInfoRepository extends CrudRepository<OtaPackageInfoE
45 45 @Param("searchText") String searchText,
46 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 49 OtaPackageInfoEntity findOtaPackageInfoById(@Param("id") UUID id);
50 50
51 51 @Query(value = "SELECT exists(SELECT * " +
... ...
... ... @@ -173,6 +173,7 @@ CREATE TABLE IF NOT EXISTS ota_package (
173 173 type varchar(32) NOT NULL,
174 174 title varchar(255) NOT NULL,
175 175 version varchar(255) NOT NULL,
  176 + tag varchar(255),
176 177 url varchar(255),
177 178 file_name varchar(255),
178 179 content_type varchar(255),
... ...
... ... @@ -188,6 +188,7 @@ CREATE TABLE IF NOT EXISTS ota_package (
188 188 type varchar(32) NOT NULL,
189 189 title varchar(255) NOT NULL,
190 190 version varchar(255) NOT NULL,
  191 + tag varchar(255),
191 192 url varchar(255),
192 193 file_name varchar(255),
193 194 content_type varchar(255),
... ...
... ... @@ -59,9 +59,10 @@ export class OtaUpdateTableConfigResolve implements Resolve<EntityTableConfig<Ot
59 59
60 60 this.config.columns.push(
61 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 66 return this.translate.instant(OtaUpdateTypeTranslationMap.get(entity.type));
66 67 }),
67 68 new EntityTableColumn<OtaPackageInfo>('url', 'ota-update.direct-url', '20%', entity => {
... ...
... ... @@ -74,6 +74,11 @@
74 74 </mat-error>
75 75 </mat-form-field>
76 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 82 <tb-device-profile-autocomplete
78 83 formControlName="deviceProfileId"
79 84 required
... ... @@ -94,8 +99,8 @@
94 99 <section *ngIf="isAdd">
95 100 <div class="mat-caption" style="margin: -8px 0 8px;" translate>ota-update.warning-after-save-no-edit</div>
96 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 104 </mat-radio-group>
100 105 </section>
101 106 <section *ngIf="!entityForm.get('isURL').value">
... ...
... ... @@ -15,7 +15,7 @@
15 15 ///
16 16
17 17 import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
18   -import { Subject } from 'rxjs';
  18 +import { combineLatest, Subject } from 'rxjs';
19 19 import { Store } from '@ngrx/store';
20 20 import { AppState } from '@core/core.state';
21 21 import { TranslateService } from '@ngx-translate/core';
... ... @@ -30,7 +30,7 @@ import {
30 30 OtaUpdateTypeTranslationMap
31 31 } from '@shared/models/ota-package.models';
32 32 import { ActionNotificationShow } from '@core/notification/notification.actions';
33   -import { filter, takeUntil } from 'rxjs/operators';
  33 +import { filter, startWith, takeUntil } from 'rxjs/operators';
34 34 import { isNotEmptyStr } from '@core/utils';
35 35
36 36 @Component({
... ... @@ -56,22 +56,33 @@ export class OtaUpdateComponent extends EntityComponent<OtaPackage> implements O
56 56
57 57 ngOnInit() {
58 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 88 ngOnDestroy() {
... ... @@ -92,6 +103,7 @@ export class OtaUpdateComponent extends EntityComponent<OtaPackage> implements O
92 103 const form = this.fb.group({
93 104 title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]],
94 105 version: [entity ? entity.version : '', [Validators.required, Validators.maxLength(255)]],
  106 + tag: [entity ? entity.tag : '', [Validators.maxLength(255)]],
95 107 type: [entity?.type ? entity.type : OtaUpdateType.FIRMWARE, Validators.required],
96 108 deviceProfileId: [entity ? entity.deviceProfileId : null, Validators.required],
97 109 checksumAlgorithm: [entity && entity.checksumAlgorithm ? entity.checksumAlgorithm : ChecksumAlgorithm.SHA256],
... ... @@ -119,6 +131,7 @@ export class OtaUpdateComponent extends EntityComponent<OtaPackage> implements O
119 131 this.entityForm.patchValue({
120 132 title: entity.title,
121 133 version: entity.version,
  134 + tag: entity.tag,
122 135 type: entity.type,
123 136 deviceProfileId: entity.deviceProfileId,
124 137 checksumAlgorithm: entity.checksumAlgorithm,
... ...
... ... @@ -91,6 +91,7 @@ export interface OtaPackageInfo extends BaseData<OtaPackageId> {
91 91 deviceProfileId?: DeviceProfileId;
92 92 title?: string;
93 93 version?: string;
  94 + tag?: string;
94 95 hasData?: boolean;
95 96 url?: string;
96 97 fileName: string;
... ...
... ... @@ -2341,8 +2341,12 @@
2341 2341 "firmware": "Firmware",
2342 2342 "software": "Software"
2343 2343 },
  2344 + "upload-binary-file": "Upload binary file",
  2345 + "use-external-url": "Use external URL",
2344 2346 "version": "Version",
2345 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 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 2352 "position": {
... ...