Commit abac309872abd59c86a35a31f2d13dbe78d7e8dc

Authored by Igor Kulikov
2 parents ea3c7139 1f621019

Merge branch 'master' of github.com:thingsboard/thingsboard

Showing 39 changed files with 295 additions and 180 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),
@@ -59,6 +59,7 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -59,6 +59,7 @@ import org.thingsboard.server.common.msg.TbMsgMetaData;
59 import org.thingsboard.server.common.msg.queue.TbCallback; 59 import org.thingsboard.server.common.msg.queue.TbCallback;
60 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; 60 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
61 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg; 61 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
  62 +import org.thingsboard.server.gen.transport.TransportProtos;
62 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; 63 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
63 import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; 64 import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg;
64 import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry; 65 import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry;
@@ -202,7 +203,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -202,7 +203,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
202 syncSessionSet.add(key); 203 syncSessionSet.add(key);
203 } 204 }
204 }); 205 });
205 - log.trace("46) Rpc syncSessionSet [{}] subscription after sent [{}]", syncSessionSet, rpcSubscriptions); 206 + log.trace("Rpc syncSessionSet [{}] subscription after sent [{}]", syncSessionSet, rpcSubscriptions);
206 syncSessionSet.forEach(rpcSubscriptions::remove); 207 syncSessionSet.forEach(rpcSubscriptions::remove);
207 } 208 }
208 209
@@ -318,7 +319,6 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -318,7 +319,6 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
318 .setOneway(request.isOneway()) 319 .setOneway(request.isOneway())
319 .setPersisted(request.isPersisted()) 320 .setPersisted(request.isPersisted())
320 .build(); 321 .build();
321 -  
322 sendToTransport(rpcRequest, sessionId, nodeId); 322 sendToTransport(rpcRequest, sessionId, nodeId);
323 }; 323 };
324 } 324 }
@@ -355,9 +355,26 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -355,9 +355,26 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
355 if (msg.hasPersistedRpcResponseMsg()) { 355 if (msg.hasPersistedRpcResponseMsg()) {
356 processPersistedRpcResponses(context, sessionInfo, msg.getPersistedRpcResponseMsg()); 356 processPersistedRpcResponses(context, sessionInfo, msg.getPersistedRpcResponseMsg());
357 } 357 }
  358 + if (msg.hasUplinkNotificationMsg()) {
  359 + processUplinkNotificationMsg(context, sessionInfo, msg.getUplinkNotificationMsg());
  360 + }
358 callback.onSuccess(); 361 callback.onSuccess();
359 } 362 }
360 363
  364 + private void processUplinkNotificationMsg(TbActorCtx context, SessionInfoProto sessionInfo, TransportProtos.UplinkNotificationMsg uplinkNotificationMsg) {
  365 + String nodeId = sessionInfo.getNodeId();
  366 + sessions.entrySet().stream()
  367 + .filter(kv -> kv.getValue().getSessionInfo().getNodeId().equals(nodeId) && (kv.getValue().isSubscribedToAttributes() || kv.getValue().isSubscribedToRPC()))
  368 + .forEach(kv -> {
  369 + ToTransportMsg msg = ToTransportMsg.newBuilder()
  370 + .setSessionIdMSB(kv.getKey().getMostSignificantBits())
  371 + .setSessionIdLSB(kv.getKey().getLeastSignificantBits())
  372 + .setUplinkNotificationMsg(uplinkNotificationMsg)
  373 + .build();
  374 + systemContext.getTbCoreToTransportService().process(kv.getValue().getSessionInfo().getNodeId(), msg);
  375 + });
  376 + }
  377 +
361 private void handleClaimDeviceMsg(TbActorCtx context, SessionInfoProto sessionInfo, ClaimDeviceMsg msg) { 378 private void handleClaimDeviceMsg(TbActorCtx context, SessionInfoProto sessionInfo, ClaimDeviceMsg msg) {
362 DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB())); 379 DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB()));
363 systemContext.getClaimDevicesService().registerClaimingInfo(tenantId, deviceId, msg.getSecretKey(), msg.getDurationMs()); 380 systemContext.getClaimDevicesService().registerClaimingInfo(tenantId, deviceId, msg.getSecretKey(), msg.getDurationMs());
@@ -599,7 +616,6 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -599,7 +616,6 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
599 616
600 void processCredentialsUpdate(TbActorMsg msg) { 617 void processCredentialsUpdate(TbActorMsg msg) {
601 if (((DeviceCredentialsUpdateNotificationMsg) msg).getDeviceCredentials().getCredentialsType() == DeviceCredentialsType.LWM2M_CREDENTIALS) { 618 if (((DeviceCredentialsUpdateNotificationMsg) msg).getDeviceCredentials().getCredentialsType() == DeviceCredentialsType.LWM2M_CREDENTIALS) {
602 - log.info("1) LwM2Mtype: ");  
603 sessions.forEach((k, v) -> { 619 sessions.forEach((k, v) -> {
604 notifyTransportAboutProfileUpdate(k, v, ((DeviceCredentialsUpdateNotificationMsg) msg).getDeviceCredentials()); 620 notifyTransportAboutProfileUpdate(k, v, ((DeviceCredentialsUpdateNotificationMsg) msg).getDeviceCredentials());
605 }); 621 });
@@ -616,7 +632,6 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -616,7 +632,6 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
616 notifyTransportAboutClosedSession(sessionId, sessionMd, "max concurrent sessions limit reached per device!"); 632 notifyTransportAboutClosedSession(sessionId, sessionMd, "max concurrent sessions limit reached per device!");
617 } 633 }
618 634
619 -  
620 private void notifyTransportAboutClosedSession(UUID sessionId, SessionInfoMetaData sessionMd, String message) { 635 private void notifyTransportAboutClosedSession(UUID sessionId, SessionInfoMetaData sessionMd, String message) {
621 SessionCloseNotificationProto sessionCloseNotificationProto = SessionCloseNotificationProto 636 SessionCloseNotificationProto sessionCloseNotificationProto = SessionCloseNotificationProto
622 .newBuilder() 637 .newBuilder()
@@ -630,7 +645,6 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -630,7 +645,6 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
630 } 645 }
631 646
632 void notifyTransportAboutProfileUpdate(UUID sessionId, SessionInfoMetaData sessionMd, DeviceCredentials deviceCredentials) { 647 void notifyTransportAboutProfileUpdate(UUID sessionId, SessionInfoMetaData sessionMd, DeviceCredentials deviceCredentials) {
633 - log.info("2) LwM2Mtype: ");  
634 ToTransportUpdateCredentialsProto.Builder notification = ToTransportUpdateCredentialsProto.newBuilder(); 648 ToTransportUpdateCredentialsProto.Builder notification = ToTransportUpdateCredentialsProto.newBuilder();
635 notification.addCredentialsId(deviceCredentials.getCredentialsId()); 649 notification.addCredentialsId(deviceCredentials.getCredentialsId());
636 notification.addCredentialsValue(deviceCredentials.getCredentialsValue()); 650 notification.addCredentialsValue(deviceCredentials.getCredentialsValue());
@@ -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<>();
@@ -36,6 +36,12 @@ public class DataConstants { @@ -36,6 +36,12 @@ public class DataConstants {
36 public static final String ALARM_CONDITION_REPEATS = "alarmConditionRepeats"; 36 public static final String ALARM_CONDITION_REPEATS = "alarmConditionRepeats";
37 public static final String ALARM_CONDITION_DURATION = "alarmConditionDuration"; 37 public static final String ALARM_CONDITION_DURATION = "alarmConditionDuration";
38 public static final String PERSISTENT = "persistent"; 38 public static final String PERSISTENT = "persistent";
  39 + public static final String COAP_TRANSPORT_NAME = "COAP";
  40 + public static final String LWM2M_TRANSPORT_NAME = "LWM2M";
  41 + public static final String MQTT_TRANSPORT_NAME = "MQTT";
  42 + public static final String HTTP_TRANSPORT_NAME = "HTTP";
  43 + public static final String SNMP_TRANSPORT_NAME = "SNMP";
  44 +
39 45
40 public static final String[] allScopes() { 46 public static final String[] allScopes() {
41 return new String[]{CLIENT_SCOPE, SHARED_SCOPE, SERVER_SCOPE}; 47 return new String[]{CLIENT_SCOPE, SHARED_SCOPE, SERVER_SCOPE};
@@ -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;
@@ -73,6 +73,7 @@ public class HashPartitionService implements PartitionService { @@ -73,6 +73,7 @@ public class HashPartitionService implements PartitionService {
73 73
74 private Map<String, TopicPartitionInfo> tbCoreNotificationTopics = new HashMap<>(); 74 private Map<String, TopicPartitionInfo> tbCoreNotificationTopics = new HashMap<>();
75 private Map<String, TopicPartitionInfo> tbRuleEngineNotificationTopics = new HashMap<>(); 75 private Map<String, TopicPartitionInfo> tbRuleEngineNotificationTopics = new HashMap<>();
  76 + private Map<String, List<ServiceInfo>> tbTransportServicesByType = new HashMap<>();
76 private List<ServiceInfo> currentOtherServices; 77 private List<ServiceInfo> currentOtherServices;
77 78
78 private HashFunction hashFunction; 79 private HashFunction hashFunction;
@@ -127,6 +128,7 @@ public class HashPartitionService implements PartitionService { @@ -127,6 +128,7 @@ public class HashPartitionService implements PartitionService {
127 128
128 @Override 129 @Override
129 public synchronized void recalculatePartitions(ServiceInfo currentService, List<ServiceInfo> otherServices) { 130 public synchronized void recalculatePartitions(ServiceInfo currentService, List<ServiceInfo> otherServices) {
  131 + tbTransportServicesByType.clear();
130 logServiceInfo(currentService); 132 logServiceInfo(currentService);
131 otherServices.forEach(this::logServiceInfo); 133 otherServices.forEach(this::logServiceInfo);
132 Map<ServiceQueueKey, List<ServiceInfo>> queueServicesMap = new HashMap<>(); 134 Map<ServiceQueueKey, List<ServiceInfo>> queueServicesMap = new HashMap<>();
@@ -229,6 +231,12 @@ public class HashPartitionService implements PartitionService { @@ -229,6 +231,12 @@ public class HashPartitionService implements PartitionService {
229 return Math.abs(hash % partitions); 231 return Math.abs(hash % partitions);
230 } 232 }
231 233
  234 + @Override
  235 + public int countTransportsByType(String type) {
  236 + var list = tbTransportServicesByType.get(type);
  237 + return list == null ? 0 : list.size();
  238 + }
  239 +
232 private Map<ServiceQueueKey, List<ServiceInfo>> getServiceKeyListMap(List<ServiceInfo> services) { 240 private Map<ServiceQueueKey, List<ServiceInfo>> getServiceKeyListMap(List<ServiceInfo> services) {
233 final Map<ServiceQueueKey, List<ServiceInfo>> currentMap = new HashMap<>(); 241 final Map<ServiceQueueKey, List<ServiceInfo>> currentMap = new HashMap<>();
234 services.forEach(serviceInfo -> { 242 services.forEach(serviceInfo -> {
@@ -332,6 +340,9 @@ public class HashPartitionService implements PartitionService { @@ -332,6 +340,9 @@ public class HashPartitionService implements PartitionService {
332 queueServiceList.computeIfAbsent(serviceQueueKey, key -> new ArrayList<>()).add(instance); 340 queueServiceList.computeIfAbsent(serviceQueueKey, key -> new ArrayList<>()).add(instance);
333 } 341 }
334 } 342 }
  343 + for (String transportType : instance.getTransportsList()) {
  344 + tbTransportServicesByType.computeIfAbsent(transportType, t -> new ArrayList<>()).add(instance);
  345 + }
335 } 346 }
336 347
337 private ServiceInfo resolveByPartitionIdx(List<ServiceInfo> servers, Integer partitionIdx) { 348 private ServiceInfo resolveByPartitionIdx(List<ServiceInfo> servers, Integer partitionIdx) {
@@ -59,4 +59,6 @@ public interface PartitionService { @@ -59,4 +59,6 @@ public interface PartitionService {
59 TopicPartitionInfo getNotificationsTopic(ServiceType serviceType, String serviceId); 59 TopicPartitionInfo getNotificationsTopic(ServiceType serviceType, String serviceId);
60 60
61 int resolvePartitionIndex(UUID entityId, int partitions); 61 int resolvePartitionIndex(UUID entityId, int partitions);
  62 +
  63 + int countTransportsByType(String type);
62 } 64 }
@@ -341,6 +341,10 @@ message ToDeviceRpcResponseMsg { @@ -341,6 +341,10 @@ message ToDeviceRpcResponseMsg {
341 string payload = 2; 341 string payload = 2;
342 } 342 }
343 343
  344 +message UplinkNotificationMsg {
  345 + int64 uplinkTs = 1;
  346 +}
  347 +
344 message ToDevicePersistedRpcResponseMsg { 348 message ToDevicePersistedRpcResponseMsg {
345 int32 requestId = 1; 349 int32 requestId = 1;
346 int64 requestIdMSB = 2; 350 int64 requestIdMSB = 2;
@@ -453,6 +457,7 @@ message TransportToDeviceActorMsg { @@ -453,6 +457,7 @@ message TransportToDeviceActorMsg {
453 ProvisionDeviceRequestMsg provisionDevice = 9; 457 ProvisionDeviceRequestMsg provisionDevice = 9;
454 ToDevicePersistedRpcResponseMsg persistedRpcResponseMsg = 10; 458 ToDevicePersistedRpcResponseMsg persistedRpcResponseMsg = 10;
455 SendPendingRPCMsg sendPendingRPC = 11; 459 SendPendingRPCMsg sendPendingRPC = 11;
  460 + UplinkNotificationMsg uplinkNotificationMsg = 12;
456 } 461 }
457 462
458 message TransportToRuleEngineMsg { 463 message TransportToRuleEngineMsg {
@@ -713,6 +718,7 @@ message ToTransportMsg { @@ -713,6 +718,7 @@ message ToTransportMsg {
713 ToTransportUpdateCredentialsProto toTransportUpdateCredentialsNotification = 11; 718 ToTransportUpdateCredentialsProto toTransportUpdateCredentialsNotification = 11;
714 ResourceUpdateMsg resourceUpdateMsg = 12; 719 ResourceUpdateMsg resourceUpdateMsg = 12;
715 ResourceDeleteMsg resourceDeleteMsg = 13; 720 ResourceDeleteMsg resourceDeleteMsg = 13;
  721 + UplinkNotificationMsg uplinkNotificationMsg = 14;
716 } 722 }
717 723
718 message UsageStatsKVProto{ 724 message UsageStatsKVProto{
@@ -22,6 +22,7 @@ import org.springframework.beans.factory.annotation.Autowired; @@ -22,6 +22,7 @@ import org.springframework.beans.factory.annotation.Autowired;
22 import org.springframework.stereotype.Service; 22 import org.springframework.stereotype.Service;
23 import org.thingsboard.server.coapserver.CoapServerService; 23 import org.thingsboard.server.coapserver.CoapServerService;
24 import org.thingsboard.server.coapserver.TbCoapServerComponent; 24 import org.thingsboard.server.coapserver.TbCoapServerComponent;
  25 +import org.thingsboard.server.common.data.DataConstants;
25 import org.thingsboard.server.common.data.TbTransportService; 26 import org.thingsboard.server.common.data.TbTransportService;
26 import org.thingsboard.server.common.data.ota.OtaPackageType; 27 import org.thingsboard.server.common.data.ota.OtaPackageType;
27 import org.thingsboard.server.transport.coap.efento.CoapEfentoTransportResource; 28 import org.thingsboard.server.transport.coap.efento.CoapEfentoTransportResource;
@@ -72,6 +73,6 @@ public class CoapTransportService implements TbTransportService { @@ -72,6 +73,6 @@ public class CoapTransportService implements TbTransportService {
72 73
73 @Override 74 @Override
74 public String getName() { 75 public String getName() {
75 - return "COAP"; 76 + return DataConstants.COAP_TRANSPORT_NAME;
76 } 77 }
77 } 78 }
@@ -18,14 +18,13 @@ package org.thingsboard.server.transport.coap.client; @@ -18,14 +18,13 @@ package org.thingsboard.server.transport.coap.client;
18 import lombok.RequiredArgsConstructor; 18 import lombok.RequiredArgsConstructor;
19 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
20 import org.eclipse.californium.core.coap.CoAP; 20 import org.eclipse.californium.core.coap.CoAP;
21 -import org.eclipse.californium.core.coap.MediaTypeRegistry;  
22 import org.eclipse.californium.core.coap.Response; 21 import org.eclipse.californium.core.coap.Response;
23 import org.eclipse.californium.core.observe.ObserveRelation; 22 import org.eclipse.californium.core.observe.ObserveRelation;
24 import org.eclipse.californium.core.server.resources.CoapExchange; 23 import org.eclipse.californium.core.server.resources.CoapExchange;
25 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 24 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
26 import org.springframework.stereotype.Service; 25 import org.springframework.stereotype.Service;
27 import org.thingsboard.server.coapserver.CoapServerContext; 26 import org.thingsboard.server.coapserver.CoapServerContext;
28 -import org.thingsboard.server.coapserver.TbCoapServerComponent; 27 +import org.thingsboard.server.common.data.DataConstants;
29 import org.thingsboard.server.common.data.Device; 28 import org.thingsboard.server.common.data.Device;
30 import org.thingsboard.server.common.data.DeviceProfile; 29 import org.thingsboard.server.common.data.DeviceProfile;
31 import org.thingsboard.server.common.data.DeviceTransportType; 30 import org.thingsboard.server.common.data.DeviceTransportType;
@@ -51,6 +50,7 @@ import org.thingsboard.server.common.transport.adaptor.AdaptorException; @@ -51,6 +50,7 @@ import org.thingsboard.server.common.transport.adaptor.AdaptorException;
51 import org.thingsboard.server.common.transport.auth.SessionInfoCreator; 50 import org.thingsboard.server.common.transport.auth.SessionInfoCreator;
52 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; 51 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
53 import org.thingsboard.server.gen.transport.TransportProtos; 52 import org.thingsboard.server.gen.transport.TransportProtos;
  53 +import org.thingsboard.server.queue.discovery.PartitionService;
54 import org.thingsboard.server.transport.coap.CoapTransportContext; 54 import org.thingsboard.server.transport.coap.CoapTransportContext;
55 import org.thingsboard.server.transport.coap.TbCoapMessageObserver; 55 import org.thingsboard.server.transport.coap.TbCoapMessageObserver;
56 import org.thingsboard.server.transport.coap.TransportConfigurationContainer; 56 import org.thingsboard.server.transport.coap.TransportConfigurationContainer;
@@ -81,6 +81,7 @@ public class DefaultCoapClientContext implements CoapClientContext { @@ -81,6 +81,7 @@ public class DefaultCoapClientContext implements CoapClientContext {
81 private final CoapTransportContext transportContext; 81 private final CoapTransportContext transportContext;
82 private final TransportService transportService; 82 private final TransportService transportService;
83 private final TransportDeviceProfileCache profileCache; 83 private final TransportDeviceProfileCache profileCache;
  84 + private final PartitionService partitionService;
84 private final ConcurrentMap<DeviceId, TbCoapClientState> clients = new ConcurrentHashMap<>(); 85 private final ConcurrentMap<DeviceId, TbCoapClientState> clients = new ConcurrentHashMap<>();
85 private final ConcurrentMap<String, TbCoapClientState> clientsByToken = new ConcurrentHashMap<>(); 86 private final ConcurrentMap<String, TbCoapClientState> clientsByToken = new ConcurrentHashMap<>();
86 87
@@ -161,7 +162,7 @@ public class DefaultCoapClientContext implements CoapClientContext { @@ -161,7 +162,7 @@ public class DefaultCoapClientContext implements CoapClientContext {
161 } 162 }
162 } 163 }
163 164
164 - private void onUplink(TbCoapClientState client) { 165 + private void onUplink(TbCoapClientState client, boolean notifyOtherServers, long uplinkTs) {
165 PowerMode powerMode = client.getPowerMode(); 166 PowerMode powerMode = client.getPowerMode();
166 PowerSavingConfiguration profileSettings = null; 167 PowerSavingConfiguration profileSettings = null;
167 if (powerMode == null) { 168 if (powerMode == null) {
@@ -174,12 +175,12 @@ public class DefaultCoapClientContext implements CoapClientContext { @@ -174,12 +175,12 @@ public class DefaultCoapClientContext implements CoapClientContext {
174 } 175 }
175 } 176 }
176 if (powerMode == null || PowerMode.DRX.equals(powerMode)) { 177 if (powerMode == null || PowerMode.DRX.equals(powerMode)) {
177 - client.updateLastUplinkTime(); 178 + client.updateLastUplinkTime(uplinkTs);
178 return; 179 return;
179 } 180 }
180 client.lock(); 181 client.lock();
181 try { 182 try {
182 - long uplinkTime = client.updateLastUplinkTime(); 183 + long uplinkTime = client.updateLastUplinkTime(uplinkTs);
183 long timeout; 184 long timeout;
184 if (PowerMode.PSM.equals(powerMode)) { 185 if (PowerMode.PSM.equals(powerMode)) {
185 Long psmActivityTimer = client.getPsmActivityTimer(); 186 Long psmActivityTimer = client.getPsmActivityTimer();
@@ -214,6 +215,9 @@ public class DefaultCoapClientContext implements CoapClientContext { @@ -214,6 +215,9 @@ public class DefaultCoapClientContext implements CoapClientContext {
214 return null; 215 return null;
215 }, timeout, TimeUnit.MILLISECONDS); 216 }, timeout, TimeUnit.MILLISECONDS);
216 client.setSleepTask(task); 217 client.setSleepTask(task);
  218 + if (notifyOtherServers && partitionService.countTransportsByType(DataConstants.COAP_TRANSPORT_NAME) > 1) {
  219 + transportService.notifyAboutUplink(getNewSyncSession(client), TransportProtos.UplinkNotificationMsg.newBuilder().setUplinkTs(uplinkTime).build(), TransportServiceCallback.EMPTY);
  220 + }
217 } finally { 221 } finally {
218 client.unlock(); 222 client.unlock();
219 } 223 }
@@ -544,6 +548,11 @@ public class DefaultCoapClientContext implements CoapClientContext { @@ -544,6 +548,11 @@ public class DefaultCoapClientContext implements CoapClientContext {
544 log.trace("[{}] Received server rpc response in the wrong session.", state.getSession()); 548 log.trace("[{}] Received server rpc response in the wrong session.", state.getSession());
545 } 549 }
546 550
  551 + @Override
  552 + public void onUplinkNotification(TransportProtos.UplinkNotificationMsg notificationMsg) {
  553 + awake(state, false, notificationMsg.getUplinkTs());
  554 + }
  555 +
547 private void cancelObserveRelation(TbCoapObservationState attrs) { 556 private void cancelObserveRelation(TbCoapObservationState attrs) {
548 if (attrs.getObserveRelation() != null) { 557 if (attrs.getObserveRelation() != null) {
549 attrs.getObserveRelation().cancel(); 558 attrs.getObserveRelation().cancel();
@@ -562,7 +571,11 @@ public class DefaultCoapClientContext implements CoapClientContext { @@ -562,7 +571,11 @@ public class DefaultCoapClientContext implements CoapClientContext {
562 571
563 @Override 572 @Override
564 public boolean awake(TbCoapClientState client) { 573 public boolean awake(TbCoapClientState client) {
565 - onUplink(client); 574 + return awake(client, true, System.currentTimeMillis());
  575 + }
  576 +
  577 + private boolean awake(TbCoapClientState client, boolean notifyOtherServers, long uplinkTs) {
  578 + onUplink(client, notifyOtherServers, uplinkTs);
566 boolean changed = compareAndSetSleepFlag(client, false); 579 boolean changed = compareAndSetSleepFlag(client, false);
567 if (changed) { 580 if (changed) {
568 log.debug("[{}] client is awake", client.getDeviceId()); 581 log.debug("[{}] client is awake", client.getDeviceId());
@@ -97,9 +97,11 @@ public class TbCoapClientState { @@ -97,9 +97,11 @@ public class TbCoapClientState {
97 lock.unlock(); 97 lock.unlock();
98 } 98 }
99 99
100 - public long updateLastUplinkTime() {  
101 - this.lastUplinkTime = System.currentTimeMillis();  
102 - this.firstEdrxDownlink = true; 100 + public long updateLastUplinkTime(long ts) {
  101 + if (ts > lastUplinkTime) {
  102 + this.lastUplinkTime = ts;
  103 + this.firstEdrxDownlink = true;
  104 + }
103 return lastUplinkTime; 105 return lastUplinkTime;
104 } 106 }
105 107
@@ -34,6 +34,7 @@ import org.springframework.web.bind.annotation.RequestMethod; @@ -34,6 +34,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
34 import org.springframework.web.bind.annotation.RequestParam; 34 import org.springframework.web.bind.annotation.RequestParam;
35 import org.springframework.web.bind.annotation.RestController; 35 import org.springframework.web.bind.annotation.RestController;
36 import org.springframework.web.context.request.async.DeferredResult; 36 import org.springframework.web.context.request.async.DeferredResult;
  37 +import org.thingsboard.server.common.data.DataConstants;
37 import org.thingsboard.server.common.data.DeviceTransportType; 38 import org.thingsboard.server.common.data.DeviceTransportType;
38 import org.thingsboard.server.common.data.TbTransportService; 39 import org.thingsboard.server.common.data.TbTransportService;
39 import org.thingsboard.server.common.data.id.DeviceId; 40 import org.thingsboard.server.common.data.id.DeviceId;
@@ -436,7 +437,7 @@ public class DeviceApiController implements TbTransportService { @@ -436,7 +437,7 @@ public class DeviceApiController implements TbTransportService {
436 437
437 @Override 438 @Override
438 public String getName() { 439 public String getName() {
439 - return "HTTP"; 440 + return DataConstants.HTTP_TRANSPORT_NAME;
440 } 441 }
441 442
442 } 443 }
@@ -28,6 +28,7 @@ import org.eclipse.leshan.server.californium.registration.CaliforniumRegistratio @@ -28,6 +28,7 @@ import org.eclipse.leshan.server.californium.registration.CaliforniumRegistratio
28 import org.eclipse.leshan.server.model.LwM2mModelProvider; 28 import org.eclipse.leshan.server.model.LwM2mModelProvider;
29 import org.springframework.stereotype.Component; 29 import org.springframework.stereotype.Component;
30 import org.thingsboard.server.cache.ota.OtaPackageDataCache; 30 import org.thingsboard.server.cache.ota.OtaPackageDataCache;
  31 +import org.thingsboard.server.common.data.DataConstants;
31 import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; 32 import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
32 import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; 33 import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
33 import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MAuthorizer; 34 import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MAuthorizer;
@@ -177,7 +178,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { @@ -177,7 +178,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
177 178
178 @Override 179 @Override
179 public String getName() { 180 public String getName() {
180 - return "LWM2M"; 181 + return DataConstants.LWM2M_TRANSPORT_NAME;
181 } 182 }
182 183
183 } 184 }
@@ -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
@@ -28,6 +28,7 @@ import org.springframework.beans.factory.annotation.Value; @@ -28,6 +28,7 @@ import org.springframework.beans.factory.annotation.Value;
28 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 28 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
29 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 29 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
30 import org.springframework.stereotype.Service; 30 import org.springframework.stereotype.Service;
  31 +import org.thingsboard.server.common.data.DataConstants;
31 import org.thingsboard.server.common.data.TbTransportService; 32 import org.thingsboard.server.common.data.TbTransportService;
32 33
33 import javax.annotation.PostConstruct; 34 import javax.annotation.PostConstruct;
@@ -114,6 +115,6 @@ public class MqttTransportService implements TbTransportService { @@ -114,6 +115,6 @@ public class MqttTransportService implements TbTransportService {
114 115
115 @Override 116 @Override
116 public String getName() { 117 public String getName() {
117 - return "MQTT"; 118 + return DataConstants.MQTT_TRANSPORT_NAME;
118 } 119 }
119 } 120 }
@@ -35,6 +35,7 @@ import org.snmp4j.transport.DefaultUdpTransportMapping; @@ -35,6 +35,7 @@ import org.snmp4j.transport.DefaultUdpTransportMapping;
35 import org.springframework.beans.factory.annotation.Value; 35 import org.springframework.beans.factory.annotation.Value;
36 import org.springframework.stereotype.Service; 36 import org.springframework.stereotype.Service;
37 import org.thingsboard.common.util.ThingsBoardThreadFactory; 37 import org.thingsboard.common.util.ThingsBoardThreadFactory;
  38 +import org.thingsboard.server.common.data.DataConstants;
38 import org.thingsboard.server.common.data.TbTransportService; 39 import org.thingsboard.server.common.data.TbTransportService;
39 import org.thingsboard.server.common.data.kv.DataType; 40 import org.thingsboard.server.common.data.kv.DataType;
40 import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec; 41 import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
@@ -300,7 +301,7 @@ public class SnmpTransportService implements TbTransportService { @@ -300,7 +301,7 @@ public class SnmpTransportService implements TbTransportService {
300 301
301 @Override 302 @Override
302 public String getName() { 303 public String getName() {
303 - return "SNMP"; 304 + return DataConstants.SNMP_TRANSPORT_NAME;
304 } 305 }
305 306
306 @PreDestroy 307 @PreDestroy
@@ -25,6 +25,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotifica @@ -25,6 +25,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotifica
25 import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg; 25 import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg;
26 import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg; 26 import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg;
27 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto; 27 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto;
  28 +import org.thingsboard.server.gen.transport.TransportProtos.UplinkNotificationMsg;
28 29
29 import java.util.Optional; 30 import java.util.Optional;
30 import java.util.UUID; 31 import java.util.UUID;
@@ -44,6 +45,8 @@ public interface SessionMsgListener { @@ -44,6 +45,8 @@ public interface SessionMsgListener {
44 45
45 void onToServerRpcResponse(ToServerRpcResponseMsg toServerResponse); 46 void onToServerRpcResponse(ToServerRpcResponseMsg toServerResponse);
46 47
  48 + default void onUplinkNotification(UplinkNotificationMsg notificationMsg){};
  49 +
47 default void onToTransportUpdateCredentials(ToTransportUpdateCredentialsProto toTransportUpdateCredentials){} 50 default void onToTransportUpdateCredentials(ToTransportUpdateCredentialsProto toTransportUpdateCredentials){}
48 51
49 default void onDeviceProfileUpdate(TransportProtos.SessionInfoProto newSessionInfo, DeviceProfile deviceProfile) {} 52 default void onDeviceProfileUpdate(TransportProtos.SessionInfoProto newSessionInfo, DeviceProfile deviceProfile) {}
@@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.DeviceTransportType; @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.DeviceTransportType;
20 import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse; 20 import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse;
21 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; 21 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
22 import org.thingsboard.server.common.transport.service.SessionMetaData; 22 import org.thingsboard.server.common.transport.service.SessionMetaData;
  23 +import org.thingsboard.server.gen.transport.TransportProtos;
23 import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; 24 import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg;
24 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; 25 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
25 import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceCredentialsRequestMsg; 26 import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceCredentialsRequestMsg;
@@ -128,4 +129,6 @@ public interface TransportService { @@ -128,4 +129,6 @@ public interface TransportService {
128 void deregisterSession(SessionInfoProto sessionInfo); 129 void deregisterSession(SessionInfoProto sessionInfo);
129 130
130 void log(SessionInfoProto sessionInfo, String msg); 131 void log(SessionInfoProto sessionInfo, String msg);
  132 +
  133 + void notifyAboutUplink(SessionInfoProto sessionInfo, TransportProtos.UplinkNotificationMsg build, TransportServiceCallback<Void> empty);
131 } 134 }
@@ -572,6 +572,14 @@ public class DefaultTransportService implements TransportService { @@ -572,6 +572,14 @@ public class DefaultTransportService implements TransportService {
572 } 572 }
573 573
574 @Override 574 @Override
  575 + public void notifyAboutUplink(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.UplinkNotificationMsg msg, TransportServiceCallback<Void> callback) {
  576 + if (checkLimits(sessionInfo, msg, callback)) {
  577 + reportActivityInternal(sessionInfo);
  578 + sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setUplinkNotificationMsg(msg).build(), callback);
  579 + }
  580 + }
  581 +
  582 + @Override
575 public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToDeviceRpcRequestMsg msg, boolean isFailedRpc, TransportServiceCallback<Void> callback) { 583 public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToDeviceRpcRequestMsg msg, boolean isFailedRpc, TransportServiceCallback<Void> callback) {
576 if (msg.getPersisted()) { 584 if (msg.getPersisted()) {
577 RpcStatus status; 585 RpcStatus status;
@@ -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),
@@ -174,14 +174,9 @@ @@ -174,14 +174,9 @@
174 <!-- <div fxLayout="column">--> 174 <!-- <div fxLayout="column">-->
175 <!-- <mat-form-field class="mat-block">--> 175 <!-- <mat-form-field class="mat-block">-->
176 <!-- <mat-label>{{ 'device-profile.lwm2m.client-strategy-label' | translate }}</mat-label>--> 176 <!-- <mat-label>{{ 'device-profile.lwm2m.client-strategy-label' | translate }}</mat-label>-->
177 -<!-- <mat-select formControlName="clientOnlyObserveAfterConnect"-->  
178 -<!-- matTooltip="{{ 'device-profile.lwm2m.client-strategy-tip' | translate:-->  
179 -<!-- { count: +lwm2mDeviceProfileFormGroup.get('clientOnlyObserveAfterConnect').value } }}"-->  
180 -<!-- matTooltipPosition="above">-->  
181 -<!-- <mat-option value=1>{{ 'device-profile.lwm2m.client-strategy-connect' | translate:-->  
182 -<!-- {count: 1} }}</mat-option>-->  
183 -<!-- <mat-option value=2>{{ 'device-profile.lwm2m.client-strategy-connect' | translate:-->  
184 -<!-- {count: 2} }}</mat-option>--> 177 +<!-- <mat-select formControlName="clientOnlyObserveAfterConnect">-->
  178 +<!-- <mat-option value=1>{{ 'device-profile.lwm2m.client-strategy-only-observe' | translate }}</mat-option>-->
  179 +<!-- <mat-option value=2>{{ 'device-profile.lwm2m.client-strategy-read-all' | translate }}</mat-option>-->
185 <!-- </mat-select>--> 180 <!-- </mat-select>-->
186 <!-- </mat-form-field>--> 181 <!-- </mat-form-field>-->
187 <!-- </div>--> 182 <!-- </div>-->
@@ -194,13 +189,12 @@ @@ -194,13 +189,12 @@
194 </mat-tab> 189 </mat-tab>
195 <mat-tab label="{{ 'device-profile.lwm2m.config-json-tab' | translate }}"> 190 <mat-tab label="{{ 'device-profile.lwm2m.config-json-tab' | translate }}">
196 <ng-template matTabContent> 191 <ng-template matTabContent>
197 - <section [formGroup]="lwm2mDeviceConfigFormGroup" style="padding: 8px 0"> 192 + <section style="padding: 8px 0">
198 <tb-json-object-edit 193 <tb-json-object-edit
199 readonly 194 readonly
200 - [required]="required"  
201 [sort]="sortFunction" 195 [sort]="sortFunction"
202 label="{{ 'device-profile.transport-type-lwm2m' | translate }}" 196 label="{{ 'device-profile.transport-type-lwm2m' | translate }}"
203 - formControlName="configurationJson"> 197 + [ngModel]="configurationValue">
204 </tb-json-object-edit> 198 </tb-json-object-edit>
205 </section> 199 </section>
206 </ng-template> 200 </ng-template>
@@ -14,7 +14,6 @@ @@ -14,7 +14,6 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { DeviceProfileTransportConfiguration } from '@shared/models/device.models';  
18 import { Component, forwardRef, Input, OnDestroy } from '@angular/core'; 17 import { Component, forwardRef, Input, OnDestroy } from '@angular/core';
19 import { 18 import {
20 ControlValueAccessor, 19 ControlValueAccessor,
@@ -76,7 +75,6 @@ import { takeUntil } from 'rxjs/operators'; @@ -76,7 +75,6 @@ import { takeUntil } from 'rxjs/operators';
76 }) 75 })
77 export class Lwm2mDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, Validator, OnDestroy { 76 export class Lwm2mDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, Validator, OnDestroy {
78 77
79 - private configurationValue: Lwm2mProfileConfigModels;  
80 private requiredValue: boolean; 78 private requiredValue: boolean;
81 private disabled = false; 79 private disabled = false;
82 private destroy$ = new Subject(); 80 private destroy$ = new Subject();
@@ -84,7 +82,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -84,7 +82,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
84 bindingModeTypes = Object.values(BingingMode); 82 bindingModeTypes = Object.values(BingingMode);
85 bindingModeTypeNamesMap = BingingModeTranslationsMap; 83 bindingModeTypeNamesMap = BingingModeTranslationsMap;
86 lwm2mDeviceProfileFormGroup: FormGroup; 84 lwm2mDeviceProfileFormGroup: FormGroup;
87 - lwm2mDeviceConfigFormGroup: FormGroup; 85 + configurationValue: Lwm2mProfileConfigModels;
88 sortFunction: (key: string, value: object) => object; 86 sortFunction: (key: string, value: object) => object;
89 87
90 get required(): boolean { 88 get required(): boolean {
@@ -128,9 +126,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -128,9 +126,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
128 compositeOperationsSupport: [false] 126 compositeOperationsSupport: [false]
129 }) 127 })
130 }); 128 });
131 - this.lwm2mDeviceConfigFormGroup = this.fb.group({  
132 - configurationJson: [null, Validators.required]  
133 - });  
134 this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateStrategy').valueChanges.pipe( 129 this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateStrategy').valueChanges.pipe(
135 takeUntil(this.destroy$) 130 takeUntil(this.destroy$)
136 ).subscribe((fwStrategy) => { 131 ).subscribe((fwStrategy) => {
@@ -158,11 +153,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -158,11 +153,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
158 ).subscribe((value) => { 153 ).subscribe((value) => {
159 this.updateDeviceProfileValue(value); 154 this.updateDeviceProfileValue(value);
160 }); 155 });
161 - this.lwm2mDeviceConfigFormGroup.valueChanges.pipe(  
162 - takeUntil(this.destroy$)  
163 - ).subscribe(() => {  
164 - this.updateModel();  
165 - });  
166 this.sortFunction = this.sortObjectKeyPathJson; 156 this.sortFunction = this.sortObjectKeyPathJson;
167 } 157 }
168 158
@@ -182,10 +172,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -182,10 +172,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
182 this.disabled = isDisabled; 172 this.disabled = isDisabled;
183 if (isDisabled) { 173 if (isDisabled) {
184 this.lwm2mDeviceProfileFormGroup.disable({emitEvent: false}); 174 this.lwm2mDeviceProfileFormGroup.disable({emitEvent: false});
185 - this.lwm2mDeviceConfigFormGroup.disable({emitEvent: false});  
186 } else { 175 } else {
187 this.lwm2mDeviceProfileFormGroup.enable({emitEvent: false}); 176 this.lwm2mDeviceProfileFormGroup.enable({emitEvent: false});
188 - this.lwm2mDeviceConfigFormGroup.enable({emitEvent: false});  
189 this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.powerMode').updateValueAndValidity({onlySelf: true}); 177 this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.powerMode').updateValueAndValidity({onlySelf: true});
190 this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateStrategy').updateValueAndValidity({onlySelf: true}); 178 this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateStrategy').updateValueAndValidity({onlySelf: true});
191 this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateStrategy').updateValueAndValidity({onlySelf: true}); 179 this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateStrategy').updateValueAndValidity({onlySelf: true});
@@ -196,9 +184,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -196,9 +184,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
196 if (isDefinedAndNotNull(value) && (value?.clientLwM2mSettings || value?.observeAttr || value?.bootstrap)) { 184 if (isDefinedAndNotNull(value) && (value?.clientLwM2mSettings || value?.observeAttr || value?.bootstrap)) {
197 this.configurationValue = value; 185 this.configurationValue = value;
198 const defaultFormSettings = !(value.observeAttr.attribute.length && value.observeAttr.telemetry.length); 186 const defaultFormSettings = !(value.observeAttr.attribute.length && value.observeAttr.telemetry.length);
199 - this.lwm2mDeviceConfigFormGroup.patchValue({  
200 - configurationJson: this.configurationValue  
201 - }, {emitEvent: defaultFormSettings});  
202 if (defaultFormSettings) { 187 if (defaultFormSettings) {
203 await this.defaultProfileConfig(); 188 await this.defaultProfileConfig();
204 } 189 }
@@ -227,9 +212,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -227,9 +212,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
227 212
228 this.configurationValue.bootstrap.bootstrapServer = bootstrap; 213 this.configurationValue.bootstrap.bootstrapServer = bootstrap;
229 this.configurationValue.bootstrap.lwm2mServer = lwm2m; 214 this.configurationValue.bootstrap.lwm2mServer = lwm2m;
230 - this.lwm2mDeviceConfigFormGroup.patchValue({  
231 - configurationJson: this.configurationValue  
232 - }, {emitEvent: false});  
233 this.lwm2mDeviceProfileFormGroup.patchValue({ 215 this.lwm2mDeviceProfileFormGroup.patchValue({
234 bootstrap: this.configurationValue.bootstrap 216 bootstrap: this.configurationValue.bootstrap
235 }, {emitEvent: false}); 217 }, {emitEvent: false});
@@ -265,6 +247,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -265,6 +247,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
265 swUpdateResource: this.configurationValue.clientLwM2mSettings.swUpdateResource || '', 247 swUpdateResource: this.configurationValue.clientLwM2mSettings.swUpdateResource || '',
266 powerMode: this.configurationValue.clientLwM2mSettings.powerMode || PowerMode.DRX, 248 powerMode: this.configurationValue.clientLwM2mSettings.powerMode || PowerMode.DRX,
267 edrxCycle: this.configurationValue.clientLwM2mSettings.edrxCycle || 0, 249 edrxCycle: this.configurationValue.clientLwM2mSettings.edrxCycle || 0,
  250 + pagingTransmissionWindow: this.configurationValue.clientLwM2mSettings.pagingTransmissionWindow || 0,
  251 + psmActivityTimer: this.configurationValue.clientLwM2mSettings.psmActivityTimer || 0,
268 compositeOperationsSupport: this.configurationValue.clientLwM2mSettings.compositeOperationsSupport || false 252 compositeOperationsSupport: this.configurationValue.clientLwM2mSettings.compositeOperationsSupport || false
269 } 253 }
270 }, 254 },
@@ -277,9 +261,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -277,9 +261,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
277 } 261 }
278 262
279 private updateModel = (): void => { 263 private updateModel = (): void => {
280 - let configuration: DeviceProfileTransportConfiguration = null;  
281 - if (this.lwm2mDeviceConfigFormGroup.valid && this.lwm2mDeviceProfileFormGroup.valid) {  
282 - configuration = this.lwm2mDeviceConfigFormGroup.value.configurationJson; 264 + let configuration: Lwm2mProfileConfigModels = null;
  265 + if (this.lwm2mDeviceProfileFormGroup.valid) {
  266 + configuration = this.configurationValue;
283 } 267 }
284 this.propagateChange(configuration); 268 this.propagateChange(configuration);
285 } 269 }
@@ -299,7 +283,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -299,7 +283,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
299 this.configurationValue.bootstrap.lwm2mServer = config.bootstrap.lwm2mServer; 283 this.configurationValue.bootstrap.lwm2mServer = config.bootstrap.lwm2mServer;
300 this.configurationValue.bootstrap.servers = config.bootstrap.servers; 284 this.configurationValue.bootstrap.servers = config.bootstrap.servers;
301 this.configurationValue.clientLwM2mSettings = config.clientLwM2mSettings; 285 this.configurationValue.clientLwM2mSettings = config.clientLwM2mSettings;
302 - this.upDateJsonAllConfig();  
303 this.updateModel(); 286 this.updateModel();
304 } 287 }
305 288
@@ -327,7 +310,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -327,7 +310,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
327 } 310 }
328 if (isDefinedAndNotNull(keyNameJson)) { 311 if (isDefinedAndNotNull(keyNameJson)) {
329 this.configurationValue.observeAttr.keyName = this.validateKeyNameObjects(keyNameJson, attributeArray, telemetryArray); 312 this.configurationValue.observeAttr.keyName = this.validateKeyNameObjects(keyNameJson, attributeArray, telemetryArray);
330 - this.upDateJsonAllConfig();  
331 this.updateKeyNameObjects(objectLwM2MS); 313 this.updateKeyNameObjects(objectLwM2MS);
332 } 314 }
333 } 315 }
@@ -513,12 +495,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -513,12 +495,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
513 return (objectsIds.size > 0) ? Array.from(objectsIds) : []; 495 return (objectsIds.size > 0) ? Array.from(objectsIds) : [];
514 } 496 }
515 497
516 - private upDateJsonAllConfig = (): void => {  
517 - this.lwm2mDeviceConfigFormGroup.patchValue({  
518 - configurationJson: this.configurationValue  
519 - }, {emitEvent: false});  
520 - }  
521 -  
522 addObjectsList = (value: ObjectLwM2M[]): void => { 498 addObjectsList = (value: ObjectLwM2M[]): void => {
523 this.updateObserveAttrTelemetryObjectFormGroup(value); 499 this.updateObserveAttrTelemetryObjectFormGroup(value);
524 } 500 }
@@ -536,7 +512,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -536,7 +512,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
536 this.removeKeyNameFromJson(value.keyId); 512 this.removeKeyNameFromJson(value.keyId);
537 this.removeAttributesFromJson(value.keyId); 513 this.removeAttributesFromJson(value.keyId);
538 this.updateObserveAttrTelemetryObjectFormGroup(objectsOld); 514 this.updateObserveAttrTelemetryObjectFormGroup(objectsOld);
539 - this.upDateJsonAllConfig();  
540 } 515 }
541 516
542 private removeObserveAttrTelemetryFromJson = (observeAttrTel: string, keyId: string): void => { 517 private removeObserveAttrTelemetryFromJson = (observeAttrTel: string, keyId: string): void => {
@@ -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;
@@ -1236,14 +1236,12 @@ @@ -1236,14 +1236,12 @@
1236 "object-list": "SEznam objektů", 1236 "object-list": "SEznam objektů",
1237 "object-list-empty": "Žádné objekty nebyly vybrány.", 1237 "object-list-empty": "Žádné objekty nebyly vybrány.",
1238 "no-objects-matching": "Žádné objekty odpovídající '{{object}}' nebyly nalezeny.", 1238 "no-objects-matching": "Žádné objekty odpovídající '{{object}}' nebyly nalezeny.",
1239 - "valid-id-instance-no-min": "Instance číslo '{{instance}}' nebyla validována. Mininimální hodnota='{{min}}'",  
1240 - "valid-id-instance-no-max": "Instance číslo '{{instance}}' nebyla validována. Maximální hodnota='{{max}}'",  
1241 - "valid-id-instance": "Instance číslo '{{instance}}' nebyla validována. { count, plural, 1 {Maximální hodnota='{{max}}'} 2 {Minimální hodnota='{{min}}'} other {Musí být pouze číslo} }",  
1242 "model-tab": "LWM2M model", 1239 "model-tab": "LWM2M model",
1243 "add-new-instances": "Přidat nové instance", 1240 "add-new-instances": "Přidat nové instance",
1244 "instances-list": "Seznam instancí", 1241 "instances-list": "Seznam instancí",
1245 - "instances-input": "Vstupní hodnota Id instance",  
1246 - "instances-input-holder": "Vstupní číslo instance...", 1242 + "instances-list-required": "Seznam instancí je povinný",
  1243 + "instance-id-pattern": "Instance číslo musí být kladné číslo.",
  1244 + "instance-id-max": "Maximální instance číslo hodnota {{max}}.",
1247 "instance": "Instance", 1245 "instance": "Instance",
1248 "resource-label": "Název zdroje #ID", 1246 "resource-label": "Název zdroje #ID",
1249 "observe-label": "Pozorování", 1247 "observe-label": "Pozorování",
@@ -1266,7 +1264,6 @@ @@ -1266,7 +1264,6 @@
1266 "view-attribute": "Zobrazit atribut", 1264 "view-attribute": "Zobrazit atribut",
1267 "remove-attribute": "Odebrat atribut", 1265 "remove-attribute": "Odebrat atribut",
1268 "mode": "Režim konfigurace bezpečnosti", 1266 "mode": "Režim konfigurace bezpečnosti",
1269 - "pattern_hex_dec": "{ count, plural, 0 {musí být v hexadecimálním formátu} other {musí být # znaků} }",  
1270 "servers": "Servery", 1267 "servers": "Servery",
1271 "short-id": "Krátké ID", 1268 "short-id": "Krátké ID",
1272 "short-id-required": "Krátké ID je povinné.", 1269 "short-id-required": "Krátké ID je povinné.",
@@ -1316,8 +1313,8 @@ @@ -1316,8 +1313,8 @@
1316 "others-tab": "Ostatní nastavení", 1313 "others-tab": "Ostatní nastavení",
1317 "client-strategy": "Strategie klienta při připojování", 1314 "client-strategy": "Strategie klienta při připojování",
1318 "client-strategy-label": "Strategie", 1315 "client-strategy-label": "Strategie",
1319 - "client-strategy-connect": "{ count, plural, 1 {1: Klientovi je odeslán pouze observe požadavek po úvodním spojení} other {2: Načti všechny zdroje a observer požadavky na klienta po registraci} }",  
1320 - "client-strategy-tip": "{ count, plural, 1 {Strategie 1: Po úvodním spojení LWM2M klienta, server odešle požadavek Observe zdrojů klientovi, přičemž tyto zdroje existující na straně LWM2M klienta jsou v profilu zařízení označeny jako pozorování.} other {Strategie 2: Po registraci, je klientovi odeslán požadavek na načtení hodnotu všech zdrojů všech objektů, které LWM2M klient má,\n poté: server odešle požadavek observe zdrojů klientovi, přičemž tyto zdroje existující na straně klienta, jsou v profilu zařízení označeny jako pozorování.} }", 1316 + "client-strategy-only-observe": "Klientovi je odeslán pouze observe požadavek po úvodním spojení",
  1317 + "client-strategy-read-all": "Načti všechny zdroje a observer požadavky na klienta po registraci",
1321 "fw-update": "Aktualizace firmware", 1318 "fw-update": "Aktualizace firmware",
1322 "fw-update-strategy": "Strategie aktualizace firmware", 1319 "fw-update-strategy": "Strategie aktualizace firmware",
1323 "fw-update-strategy-data": "Odeslat (push) aktualizaci firmware jako binární soubor pomocí Object 19 a Resource 0 (Data)", 1320 "fw-update-strategy-data": "Odeslat (push) aktualizaci firmware jako binární soubor pomocí Object 19 a Resource 0 (Data)",
@@ -1273,7 +1273,6 @@ @@ -1273,7 +1273,6 @@
1273 "view-attribute": "View attribute", 1273 "view-attribute": "View attribute",
1274 "remove-attribute": "Remove attribute", 1274 "remove-attribute": "Remove attribute",
1275 "mode": "Security config mode", 1275 "mode": "Security config mode",
1276 - "pattern_hex_dec": "{ count, plural, 0 {must be hex decimal format} other {must be # characters} }",  
1277 "servers": "Servers", 1276 "servers": "Servers",
1278 "short-id": "Short ID", 1277 "short-id": "Short ID",
1279 "short-id-required": "Short ID is required.", 1278 "short-id-required": "Short ID is required.",
@@ -1323,8 +1322,8 @@ @@ -1323,8 +1322,8 @@
1323 "others-tab": "Other settings", 1322 "others-tab": "Other settings",
1324 "client-strategy": "Client strategy when connecting", 1323 "client-strategy": "Client strategy when connecting",
1325 "client-strategy-label": "Strategy", 1324 "client-strategy-label": "Strategy",
1326 - "client-strategy-connect": "{ count, plural, 1 {1: Only Observe Request to the client after the initial connection} other {2: Read All Resources & Observe Request to the client after registration} }",  
1327 - "client-strategy-tip": "{ count, plural, 1 {Strategy 1: After the initial connection of the LWM2M Client, the server sends Observe resources Request to the client, those resources that are marked as observation in the Device profile and which exist on the LWM2M client.} other {Strategy 2: After the registration, request the client to read all the resource values for all objects that the LWM2M client has,\n then execute: the server sends Observe resources Request to the client, those resources that are marked as observation in the Device profile and which exist on the LWM2M client.} }", 1325 + "client-strategy-only-observe": "Only Observe Request to the client after the initial connection",
  1326 + "client-strategy-read-all": "Read All Resources & Observe Request to the client after registration",
1328 "fw-update": "Firmware update", 1327 "fw-update": "Firmware update",
1329 "fw-update-strategy": "Firmware update strategy", 1328 "fw-update-strategy": "Firmware update strategy",
1330 "fw-update-strategy-data": "Push firmware update as binary file using Object 19 and Resource 0 (Data)", 1329 "fw-update-strategy-data": "Push firmware update as binary file using Object 19 and Resource 0 (Data)",
@@ -2342,8 +2341,12 @@ @@ -2342,8 +2341,12 @@
2342 "firmware": "Firmware", 2341 "firmware": "Firmware",
2343 "software": "Software" 2342 "software": "Software"
2344 }, 2343 },
  2344 + "upload-binary-file": "Upload binary file",
  2345 + "use-external-url": "Use external URL",
2345 "version": "Version", 2346 "version": "Version",
2346 "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.",
2347 "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."
2348 }, 2351 },
2349 "position": { 2352 "position": {