Commit d190cba27754891dff50aec8f2eae29e2af13964

Authored by Andrew Shvayka
Committed by GitHub
2 parents 9bd4bf0e 42898d4a

Merge pull request #4787 from YevhenBondarenko/feature/power-mode

Feature/power mode
Showing 32 changed files with 302 additions and 235 deletions
... ... @@ -41,6 +41,8 @@ import org.thingsboard.server.common.data.TbResource;
41 41 import org.thingsboard.server.common.data.TenantProfile;
42 42 import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials;
43 43 import org.thingsboard.server.common.data.device.credentials.ProvisionDeviceCredentialsData;
  44 +import org.thingsboard.server.common.data.device.data.Lwm2mDeviceTransportConfiguration;
  45 +import org.thingsboard.server.common.data.device.data.PowerMode;
44 46 import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileCredentials;
45 47 import org.thingsboard.server.common.data.id.CustomerId;
46 48 import org.thingsboard.server.common.data.id.DeviceId;
... ... @@ -459,7 +461,14 @@ public class DefaultTransportApiService implements TransportApiService {
459 461 }
460 462
461 463 private DeviceInfoProto getDeviceInfoProto(Device device) throws JsonProcessingException {
462   - return DeviceInfoProto.newBuilder()
  464 + PowerMode powerMode = null;
  465 + switch (device.getDeviceData().getTransportConfiguration().getType()) {
  466 + case LWM2M:
  467 + powerMode = ((Lwm2mDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration()).getPowerMode();
  468 + break;
  469 + }
  470 +
  471 + DeviceInfoProto.Builder builder = DeviceInfoProto.newBuilder()
463 472 .setTenantIdMSB(device.getTenantId().getId().getMostSignificantBits())
464 473 .setTenantIdLSB(device.getTenantId().getId().getLeastSignificantBits())
465 474 .setCustomerIdMSB(Optional.ofNullable(device.getCustomerId()).map(customerId -> customerId.getId().getMostSignificantBits()).orElse(0L))
... ... @@ -470,8 +479,11 @@ public class DefaultTransportApiService implements TransportApiService {
470 479 .setDeviceType(device.getType())
471 480 .setDeviceProfileIdMSB(device.getDeviceProfileId().getId().getMostSignificantBits())
472 481 .setDeviceProfileIdLSB(device.getDeviceProfileId().getId().getLeastSignificantBits())
473   - .setAdditionalInfo(mapper.writeValueAsString(device.getAdditionalInfo()))
474   - .build();
  482 + .setAdditionalInfo(mapper.writeValueAsString(device.getAdditionalInfo()));
  483 + if (powerMode != null) {
  484 + builder.setPowerMode(powerMode.name());
  485 + }
  486 + return builder.build();
475 487 }
476 488
477 489 private ListenableFuture<TransportApiResponseMsg> getEmptyTransportApiResponseFuture() {
... ...
... ... @@ -20,9 +20,6 @@ import com.fasterxml.jackson.annotation.JsonAnySetter;
20 20 import com.fasterxml.jackson.annotation.JsonIgnore;
21 21 import lombok.Data;
22 22 import org.thingsboard.server.common.data.DeviceTransportType;
23   -import org.thingsboard.server.common.data.device.data.lwm2m.BootstrapConfiguration;
24   -import org.thingsboard.server.common.data.device.data.lwm2m.OtherConfiguration;
25   -import org.thingsboard.server.common.data.device.data.lwm2m.TelemetryMappingConfiguration;
26 23
27 24 import java.util.HashMap;
28 25 import java.util.Map;
... ... @@ -30,6 +27,8 @@ import java.util.Map;
30 27 @Data
31 28 public class Lwm2mDeviceTransportConfiguration implements DeviceTransportConfiguration {
32 29
  30 + private PowerMode powerMode;
  31 +
33 32 @JsonIgnore
34 33 private Map<String, Object> properties = new HashMap<>();
35 34
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.device.data;
  17 +
  18 +public enum PowerMode {
  19 + PSM, DRX, E_DRX
  20 +}
... ...
... ... @@ -16,6 +16,7 @@
16 16 package org.thingsboard.server.common.data.device.data.lwm2m;
17 17
18 18 import lombok.Data;
  19 +import org.thingsboard.server.common.data.device.data.PowerMode;
19 20
20 21 @Data
21 22 public class OtherConfiguration {
... ... @@ -23,7 +24,8 @@ public class OtherConfiguration {
23 24 private Integer fwUpdateStrategy;
24 25 private Integer swUpdateStrategy;
25 26 private Integer clientOnlyObserveAfterConnect;
26   - private String fwUpdateRecourse;
27   - private String swUpdateRecourse;
  27 + private PowerMode powerMode;
  28 + private String fwUpdateResource;
  29 + private String swUpdateResource;
28 30
29 31 }
... ...
... ... @@ -115,6 +115,7 @@ message DeviceInfoProto {
115 115 int64 deviceProfileIdLSB = 9;
116 116 int64 customerIdMSB = 10;
117 117 int64 customerIdLSB = 11;
  118 + string powerMode = 12;
118 119 }
119 120
120 121 /**
... ...
... ... @@ -44,7 +44,6 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportC
44 44 import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration;
45 45 import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
46 46 import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
47   -import org.thingsboard.server.common.data.rpc.RpcStatus;
48 47 import org.thingsboard.server.common.data.security.DeviceTokenCredentials;
49 48 import org.thingsboard.server.common.msg.session.FeatureType;
50 49 import org.thingsboard.server.common.msg.session.SessionMsgType;
... ... @@ -509,23 +508,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
509 508 closeObserveRelationAndNotify(sessionId, CoAP.ResponseCode.INTERNAL_SERVER_ERROR);
510 509 successful = false;
511 510 } finally {
512   - if (msg.getPersisted()) {
513   - RpcStatus status;
514   - if (!successful) {
515   - status = RpcStatus.FAILED;
516   - } else if (msg.getOneway()) {
517   - status = RpcStatus.SUCCESSFUL;
518   - } else {
519   - status = RpcStatus.DELIVERED;
520   - }
521   - TransportProtos.ToDevicePersistedRpcResponseMsg responseMsg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder()
522   - .setRequestId(msg.getRequestId())
523   - .setRequestIdLSB(msg.getRequestIdLSB())
524   - .setRequestIdMSB(msg.getRequestIdMSB())
525   - .setStatus(status.name())
526   - .build();
527   - coapTransportResource.transportService.process(sessionInfo, responseMsg, TransportServiceCallback.EMPTY);
528   - }
  511 + coapTransportResource.transportService.process(sessionInfo, msg, !successful, TransportServiceCallback.EMPTY);
529 512 if (!successful) {
530 513 closeAndDeregister();
531 514 }
... ...
... ... @@ -408,21 +408,7 @@ public class DeviceApiController implements TbTransportService {
408 408 public void onToDeviceRpcRequest(UUID sessionId, ToDeviceRpcRequestMsg msg) {
409 409 log.trace("[{}] Received RPC command to device", sessionId);
410 410 responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg, true).toString(), HttpStatus.OK));
411   - if (msg.getPersisted()) {
412   - RpcStatus status;
413   - if (msg.getOneway()) {
414   - status = RpcStatus.SUCCESSFUL;
415   - } else {
416   - status = RpcStatus.DELIVERED;
417   - }
418   - TransportProtos.ToDevicePersistedRpcResponseMsg responseMsg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder()
419   - .setRequestId(msg.getRequestId())
420   - .setRequestIdLSB(msg.getRequestIdLSB())
421   - .setRequestIdMSB(msg.getRequestIdMSB())
422   - .setStatus(status.name())
423   - .build();
424   - transportService.process(sessionInfo, responseMsg, TransportServiceCallback.EMPTY);
425   - }
  411 + transportService.process(sessionInfo, msg, false, TransportServiceCallback.EMPTY);
426 412 }
427 413
428 414 @Override
... ...
... ... @@ -33,6 +33,7 @@ import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
33 33 import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
34 34 import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
35 35 import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
  36 +import org.thingsboard.server.transport.lwm2m.server.client.LwM2MAuthException;
36 37 import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer;
37 38
38 39 import java.io.IOException;
... ... @@ -84,7 +85,14 @@ public class LwM2mCredentialsSecurityInfoValidator {
84 85 } catch (InterruptedException e) {
85 86 log.error("Failed to await credentials!", e);
86 87 }
87   - return resultSecurityStore[0];
  88 +
  89 + TbLwM2MSecurityInfo securityInfo = resultSecurityStore[0];
  90 +
  91 + if (securityInfo.getSecurityMode() == null) {
  92 + throw new LwM2MAuthException();
  93 + }
  94 +
  95 + return securityInfo;
88 96 }
89 97
90 98 /**
... ...
... ... @@ -16,6 +16,7 @@
16 16 package org.thingsboard.server.transport.lwm2m.secure;
17 17
18 18 import lombok.RequiredArgsConstructor;
  19 +import lombok.extern.slf4j.Slf4j;
19 20 import org.eclipse.leshan.core.request.Identity;
20 21 import org.eclipse.leshan.core.request.UplinkRequest;
21 22 import org.eclipse.leshan.server.registration.Registration;
... ... @@ -24,14 +25,15 @@ import org.eclipse.leshan.server.security.SecurityChecker;
24 25 import org.eclipse.leshan.server.security.SecurityInfo;
25 26 import org.springframework.stereotype.Component;
26 27 import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
  28 +import org.thingsboard.server.transport.lwm2m.server.client.LwM2MAuthException;
27 29 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
28 30 import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore;
29   -import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2mSecurityStore;
30 31 import org.thingsboard.server.transport.lwm2m.server.store.TbSecurityStore;
31 32
32 33 @Component
33 34 @RequiredArgsConstructor
34 35 @TbLwM2mTransportComponent
  36 +@Slf4j
35 37 public class TbLwM2MAuthorizer implements Authorizer {
36 38
37 39 private final TbLwM2MDtlsSessionStore sessionStorage;
... ... @@ -57,7 +59,12 @@ public class TbLwM2MAuthorizer implements Authorizer {
57 59 }
58 60 SecurityInfo expectedSecurityInfo = null;
59 61 if (securityStore != null) {
60   - expectedSecurityInfo = securityStore.getByEndpoint(registration.getEndpoint());
  62 + try {
  63 + expectedSecurityInfo = securityStore.getByEndpoint(registration.getEndpoint());
  64 + } catch (LwM2MAuthException e) {
  65 + log.warn("Registration failed: FORBIDDEN, endpointId: [{}]", registration.getEndpoint());
  66 + return null;
  67 + }
61 68 }
62 69 if (securityChecker.checkSecurityInfo(registration.getEndpoint(), senderIdentity, expectedSecurityInfo)) {
63 70 return registration;
... ...
... ... @@ -42,6 +42,7 @@ import org.thingsboard.server.common.transport.util.SslUtil;
42 42 import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
43 43 import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
44 44 import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
  45 +import org.thingsboard.server.transport.lwm2m.server.client.LwM2MAuthException;
45 46 import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore;
46 47 import org.thingsboard.server.transport.lwm2m.server.store.TbMainSecurityStore;
47 48
... ... @@ -118,7 +119,12 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer
118 119
119 120 String strCert = SslUtil.getCertificateString(cert);
120 121 String sha3Hash = EncryptionUtil.getSha3Hash(strCert);
121   - TbLwM2MSecurityInfo securityInfo = securityInfoValidator.getEndpointSecurityInfoByCredentialsId(sha3Hash, CLIENT);
  122 + TbLwM2MSecurityInfo securityInfo;
  123 + try {
  124 + securityInfo = securityInfoValidator.getEndpointSecurityInfoByCredentialsId(sha3Hash, CLIENT);
  125 + } catch (LwM2MAuthException e) {
  126 + securityInfo = null;
  127 + }
122 128 ValidateDeviceCredentialsResponse msg = securityInfo != null ? securityInfo.getMsg() : null;
123 129 if (msg != null && org.thingsboard.server.common.data.StringUtils.isNotEmpty(msg.getCredentials())) {
124 130 LwM2MCredentials credentials = JacksonUtil.fromString(msg.getCredentials(), LwM2MCredentials.class);
... ...
... ... @@ -65,7 +65,7 @@ import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE
65 65 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256;
66 66 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8;
67 67 import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.getCoapConfig;
68   -import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FIRMWARE_UPDATE_COAP_RECOURSE;
  68 +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FIRMWARE_UPDATE_COAP_RESOURCE;
69 69
70 70 @Slf4j
71 71 @Component
... ... @@ -105,7 +105,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
105 105 * "coap://host:port/{path}/{token}/{nameFile}"
106 106 */
107 107
108   - LwM2mTransportCoapResource otaCoapResource = new LwM2mTransportCoapResource(otaPackageDataCache, FIRMWARE_UPDATE_COAP_RECOURSE);
  108 + LwM2mTransportCoapResource otaCoapResource = new LwM2mTransportCoapResource(otaPackageDataCache, FIRMWARE_UPDATE_COAP_RESOURCE);
109 109 this.server.coap().getServer().add(otaCoapResource);
110 110 this.startLhServer();
111 111 this.context.setServer(server);
... ...
... ... @@ -23,7 +23,6 @@ import org.jetbrains.annotations.NotNull;
23 23 import org.thingsboard.server.common.data.Device;
24 24 import org.thingsboard.server.common.data.DeviceProfile;
25 25 import org.thingsboard.server.common.data.ResourceType;
26   -import org.thingsboard.server.common.data.rpc.RpcStatus;
27 26 import org.thingsboard.server.common.transport.SessionMsgListener;
28 27 import org.thingsboard.server.common.transport.TransportService;
29 28 import org.thingsboard.server.common.transport.TransportServiceCallback;
... ... @@ -85,21 +84,7 @@ public class LwM2mSessionMsgListener implements GenericFutureListener<Future<? s
85 84 public void onToDeviceRpcRequest(UUID sessionId, ToDeviceRpcRequestMsg toDeviceRequest) {
86 85 log.trace("[{}] Received RPC command to device", sessionId);
87 86 this.rpcHandler.onToDeviceRpcRequest(toDeviceRequest, this.sessionInfo);
88   - if (toDeviceRequest.getPersisted()) {
89   - RpcStatus status;
90   - if (toDeviceRequest.getOneway()) {
91   - status = RpcStatus.SUCCESSFUL;
92   - } else {
93   - status = RpcStatus.DELIVERED;
94   - }
95   - TransportProtos.ToDevicePersistedRpcResponseMsg responseMsg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder()
96   - .setRequestId(toDeviceRequest.getRequestId())
97   - .setRequestIdLSB(toDeviceRequest.getRequestIdLSB())
98   - .setRequestIdMSB(toDeviceRequest.getRequestIdMSB())
99   - .setStatus(status.name())
100   - .build();
101   - transportService.process(sessionInfo, responseMsg, TransportServiceCallback.EMPTY);
102   - }
  87 + transportService.process(sessionInfo, toDeviceRequest, false, TransportServiceCallback.EMPTY);
103 88 }
104 89
105 90 @Override
... ...
... ... @@ -31,8 +31,8 @@ import java.util.concurrent.ConcurrentHashMap;
31 31 import java.util.concurrent.ConcurrentMap;
32 32 import java.util.concurrent.atomic.AtomicInteger;
33 33
34   -import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FIRMWARE_UPDATE_COAP_RECOURSE;
35   -import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SOFTWARE_UPDATE_COAP_RECOURSE;
  34 +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FIRMWARE_UPDATE_COAP_RESOURCE;
  35 +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SOFTWARE_UPDATE_COAP_RESOURCE;
36 36
37 37 @Slf4j
38 38 public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource {
... ... @@ -71,8 +71,8 @@ public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource {
71 71 protected void processHandleGet(CoapExchange exchange) {
72 72 log.warn("90) processHandleGet [{}]", exchange);
73 73 if (exchange.getRequestOptions().getUriPath().size() >= 2 &&
74   - (FIRMWARE_UPDATE_COAP_RECOURSE.equals(exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size()-2)) ||
75   - SOFTWARE_UPDATE_COAP_RECOURSE.equals(exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size()-2)))) {
  74 + (FIRMWARE_UPDATE_COAP_RESOURCE.equals(exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size() - 2)) ||
  75 + SOFTWARE_UPDATE_COAP_RESOURCE.equals(exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size() - 2)))) {
76 76 this.sendOtaData(exchange);
77 77 }
78 78 }
... ... @@ -131,7 +131,7 @@ public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource {
131 131 }
132 132
133 133 private void sendOtaData(CoapExchange exchange) {
134   - String idStr = exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size()-1
  134 + String idStr = exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size() - 1
135 135 );
136 136 UUID currentId = UUID.fromString(idStr);
137 137 Response response = new Response(CoAP.ResponseCode.CONTENT);
... ... @@ -144,8 +144,7 @@ public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource {
144 144 boolean lastFlag = fwData.length <= chunkSize;
145 145 response.getOptions().setBlock2(chunkSize, lastFlag, 0);
146 146 log.warn("92) with blokc2 Send currentId: [{}], length: [{}], chunkSize [{}], moreFlag [{}]", currentId.toString(), fwData.length, chunkSize, lastFlag);
147   - }
148   - else {
  147 + } else {
149 148 log.warn("92) with block1 Send currentId: [{}], length: [{}], ", currentId.toString(), fwData.length);
150 149 }
151 150 exchange.respond(response);
... ...
... ... @@ -123,7 +123,7 @@ public class LwM2mTransportUtil {
123 123 }
124 124 }
125 125
126   - public static boolean equalsResourceValue(Object valueOld, Object valueNew, ResourceModel.Type type, LwM2mPath
  126 + public static boolean equalsResourceValue(Object valueOld, Object valueNew, ResourceModel.Type type, LwM2mPath
127 127 resourcePath) throws CodecException {
128 128 switch (type) {
129 129 case BOOLEAN:
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server.client;
  17 +
  18 +public class LwM2MAuthException extends RuntimeException {
  19 +
  20 + private static final long serialVersionUID = 4202690897971364044L;
  21 +
  22 +}
... ...
... ... @@ -32,6 +32,8 @@ import org.eclipse.leshan.server.registration.Registration;
32 32 import org.eclipse.leshan.server.security.SecurityInfo;
33 33 import org.thingsboard.server.common.data.Device;
34 34 import org.thingsboard.server.common.data.DeviceProfile;
  35 +import org.thingsboard.server.common.data.device.data.Lwm2mDeviceTransportConfiguration;
  36 +import org.thingsboard.server.common.data.device.data.PowerMode;
35 37 import org.thingsboard.server.common.data.id.TenantId;
36 38 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
37 39 import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
... ... @@ -80,6 +82,9 @@ public class LwM2mClient implements Cloneable {
80 82 private String deviceProfileName;
81 83
82 84 @Getter
  85 + private PowerMode powerMode;
  86 +
  87 + @Getter
83 88 private String identity;
84 89 @Getter
85 90 private SecurityInfo securityInfo;
... ... @@ -121,6 +126,7 @@ public class LwM2mClient implements Cloneable {
121 126 this.profileId = new UUID(session.getDeviceProfileIdMSB(), session.getDeviceProfileIdLSB());
122 127 this.deviceName = session.getDeviceName();
123 128 this.deviceProfileName = session.getDeviceType();
  129 + this.powerMode = credentials.getDeviceInfo().getPowerMode();
124 130 }
125 131
126 132 public void lock() {
... ... @@ -140,6 +146,7 @@ public class LwM2mClient implements Cloneable {
140 146 builder.setDeviceName(deviceName);
141 147 deviceProfileOpt.ifPresent(deviceProfile -> updateSession(deviceProfile, builder));
142 148 this.session = builder.build();
  149 + this.powerMode = ((Lwm2mDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration()).getPowerMode();
143 150 }
144 151
145 152 public void onDeviceProfileUpdate(DeviceProfile deviceProfile) {
... ...
... ... @@ -87,8 +87,8 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
87 87 public static final String SOFTWARE_TITLE = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE);
88 88 public static final String SOFTWARE_URL = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.URL);
89 89
90   - public static final String FIRMWARE_UPDATE_COAP_RECOURSE = "tbfw";
91   - public static final String SOFTWARE_UPDATE_COAP_RECOURSE = "tbsw";
  90 + public static final String FIRMWARE_UPDATE_COAP_RESOURCE = "tbfw";
  91 + public static final String SOFTWARE_UPDATE_COAP_RESOURCE = "tbsw";
92 92 private static final String FW_PACKAGE_5_ID = "/5/0/0";
93 93 private static final String FW_URL_ID = "/5/0/1";
94 94 private static final String FW_EXECUTE_ID = "/5/0/2";
... ... @@ -206,7 +206,7 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
206 206 log.debug("[{}] Current fw strategy: {}", client.getEndpoint(), configuration.getFwUpdateStrategy());
207 207 LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client);
208 208 fwInfo.setFwStrategy(LwM2MFirmwareUpdateStrategy.fromStrategyFwByCode(configuration.getFwUpdateStrategy()));
209   - fwInfo.setBaseUrl(configuration.getFwUpdateRecourse());
  209 + fwInfo.setBaseUrl(configuration.getFwUpdateResource());
210 210 startFirmwareUpdateIfNeeded(client, fwInfo);
211 211 }
212 212
... ... @@ -215,7 +215,7 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
215 215 log.debug("[{}] Current sw strategy: {}", client.getEndpoint(), configuration.getSwUpdateStrategy());
216 216 LwM2MClientOtaInfo swInfo = getOrInitSwInfo(client);
217 217 swInfo.setSwStrategy(LwM2MSoftwareUpdateStrategy.fromStrategySwByCode(configuration.getSwUpdateStrategy()));
218   - swInfo.setBaseUrl(configuration.getSwUpdateRecourse());
  218 + swInfo.setBaseUrl(configuration.getSwUpdateResource());
219 219 startSoftwareUpdateIfNeeded(client, swInfo);
220 220 }
221 221
... ... @@ -339,7 +339,7 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
339 339 downlinkHandler.sendWriteReplaceRequest(client, writeRequest, new TbLwM2MWriteResponseCallback(uplinkHandler, logService, client, versionedId));
340 340 break;
341 341 case OBJ_5_TEMP_URL:
342   - startFirmwareUpdateUsingUrl(client, fwInfo.getBaseUrl() + "/" + FIRMWARE_UPDATE_COAP_RECOURSE + "/" + otaPackageId.toString());
  342 + startFirmwareUpdateUsingUrl(client, fwInfo.getBaseUrl() + "/" + FIRMWARE_UPDATE_COAP_RESOURCE + "/" + otaPackageId.toString());
343 343 break;
344 344 default:
345 345 sendStateUpdateToTelemetry(client, fwInfo, OtaPackageUpdateStatus.FAILED, "Unsupported strategy: " + strategy.name());
... ... @@ -382,7 +382,7 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
382 382 return this.fwStates.computeIfAbsent(client.getEndpoint(), endpoint -> {
383 383 var profile = clientContext.getProfile(client.getProfileId());
384 384 return new LwM2MClientOtaInfo(endpoint, OtaPackageType.FIRMWARE, profile.getClientLwM2mSettings().getFwUpdateStrategy(),
385   - profile.getClientLwM2mSettings().getFwUpdateRecourse());
  385 + profile.getClientLwM2mSettings().getFwUpdateResource());
386 386 });
387 387 }
388 388
... ... @@ -390,7 +390,7 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
390 390 //TODO: fetch state from the cache or DB.
391 391 return swStates.computeIfAbsent(client.getEndpoint(), endpoint -> {
392 392 var profile = clientContext.getProfile(client.getProfileId());
393   - return new LwM2MClientOtaInfo(endpoint, OtaPackageType.SOFTWARE, profile.getClientLwM2mSettings().getSwUpdateStrategy(), profile.getClientLwM2mSettings().getSwUpdateRecourse());
  393 + return new LwM2MClientOtaInfo(endpoint, OtaPackageType.SOFTWARE, profile.getClientLwM2mSettings().getSwUpdateStrategy(), profile.getClientLwM2mSettings().getSwUpdateResource());
394 394 });
395 395
396 396 }
... ...
... ... @@ -37,10 +37,10 @@ import org.eclipse.leshan.server.registration.Registration;
37 37 import org.springframework.context.annotation.Lazy;
38 38 import org.springframework.stereotype.Service;
39 39 import org.thingsboard.common.util.DonAsynchron;
40   -import org.thingsboard.server.cache.ota.OtaPackageDataCache;
41 40 import org.thingsboard.server.common.data.Device;
42 41 import org.thingsboard.server.common.data.DeviceProfile;
43 42 import org.thingsboard.server.common.data.StringUtils;
  43 +import org.thingsboard.server.common.data.device.data.PowerMode;
44 44 import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes;
45 45 import org.thingsboard.server.common.data.device.data.lwm2m.OtherConfiguration;
46 46 import org.thingsboard.server.common.data.device.data.lwm2m.TelemetryMappingConfiguration;
... ... @@ -83,8 +83,6 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttrib
83 83 import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesRequest;
84 84 import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
85 85 import org.thingsboard.server.transport.lwm2m.server.ota.LwM2MOtaUpdateService;
86   -import org.thingsboard.server.transport.lwm2m.server.ota.firmware.LwM2MFirmwareUpdateStrategy;
87   -import org.thingsboard.server.transport.lwm2m.server.ota.software.LwM2MSoftwareUpdateStrategy;
88 86 import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2MRpcRequestHandler;
89 87 import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore;
90 88 import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
... ... @@ -203,30 +201,25 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl
203 201 executor.submit(() -> {
204 202 LwM2mClient lwM2MClient = this.clientContext.getClientByEndpoint(registration.getEndpoint());
205 203 try {
206   - log.warn("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId());
207   - if (lwM2MClient != null) {
208   - Optional<SessionInfoProto> oldSessionInfo = this.clientContext.register(lwM2MClient, registration);
209   - if (oldSessionInfo.isPresent()) {
210   - log.info("[{}] Closing old session: {}", registration.getEndpoint(), new UUID(oldSessionInfo.get().getSessionIdMSB(), oldSessionInfo.get().getSessionIdLSB()));
211   - closeSession(oldSessionInfo.get());
212   - }
213   - logService.log(lwM2MClient, LOG_LWM2M_INFO + ": Client registered with registration id: " + registration.getId());
214   - SessionInfoProto sessionInfo = lwM2MClient.getSession();
215   - transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, attributesService, rpcHandler, sessionInfo, transportService));
216   - log.warn("40) sessionId [{}] Registering rpc subscription after Registration client", new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
217   - TransportProtos.TransportToDeviceActorMsg msg = TransportProtos.TransportToDeviceActorMsg.newBuilder()
218   - .setSessionInfo(sessionInfo)
219   - .setSessionEvent(DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN))
220   - .setSubscribeToAttributes(TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().setSessionType(TransportProtos.SessionType.ASYNC).build())
221   - .setSubscribeToRPC(TransportProtos.SubscribeToRPCMsg.newBuilder().setSessionType(TransportProtos.SessionType.ASYNC).build())
222   - .build();
223   - transportService.process(msg, null);
224   - this.initClientTelemetry(lwM2MClient);
225   - this.initAttributes(lwM2MClient);
226   - otaService.init(lwM2MClient);
227   - } else {
228   - log.error("Client: [{}] onRegistered [{}] name [{}] lwM2MClient ", registration.getId(), registration.getEndpoint(), null);
  204 + log.debug("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId());
  205 + Optional<SessionInfoProto> oldSessionInfo = this.clientContext.register(lwM2MClient, registration);
  206 + if (oldSessionInfo.isPresent()) {
  207 + log.info("[{}] Closing old session: {}", registration.getEndpoint(), new UUID(oldSessionInfo.get().getSessionIdMSB(), oldSessionInfo.get().getSessionIdLSB()));
  208 + closeSession(oldSessionInfo.get());
229 209 }
  210 + logService.log(lwM2MClient, LOG_LWM2M_INFO + ": Client registered with registration id: " + registration.getId());
  211 + SessionInfoProto sessionInfo = lwM2MClient.getSession();
  212 + transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, attributesService, rpcHandler, sessionInfo, transportService));
  213 + TransportProtos.TransportToDeviceActorMsg msg = TransportProtos.TransportToDeviceActorMsg.newBuilder()
  214 + .setSessionInfo(sessionInfo)
  215 + .setSessionEvent(DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN))
  216 + .setSubscribeToAttributes(TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().setSessionType(TransportProtos.SessionType.ASYNC).build())
  217 + .setSubscribeToRPC(TransportProtos.SubscribeToRPCMsg.newBuilder().setSessionType(TransportProtos.SessionType.ASYNC).build())
  218 + .build();
  219 + transportService.process(msg, null);
  220 + this.initClientTelemetry(lwM2MClient);
  221 + this.initAttributes(lwM2MClient);
  222 + otaService.init(lwM2MClient);
230 223 } catch (LwM2MClientStateException stateException) {
231 224 if (LwM2MClientState.UNREGISTERED.equals(stateException.getState())) {
232 225 log.info("[{}] retry registration due to race condition: [{}].", registration.getEndpoint(), stateException.getState());
... ... @@ -410,6 +403,26 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl
410 403 log.trace("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint());
411 404 logService.log(clientContext.getClientByEndpoint(registration.getEndpoint()), LOG_LWM2M_INFO + ": Client is awake!");
412 405 //TODO: associate endpointId with device information.
  406 +
  407 + LwM2mClient lwM2MClient = this.clientContext.getClientByEndpoint(registration.getEndpoint());
  408 +
  409 + if (LwM2MClientState.REGISTERED.equals(lwM2MClient.getState())) {
  410 + PowerMode powerMode = lwM2MClient.getPowerMode();
  411 + if (powerMode == null) {
  412 + Lwm2mDeviceProfileTransportConfiguration deviceProfile = clientContext.getProfile(lwM2MClient.getProfileId());
  413 + powerMode = deviceProfile.getClientLwM2mSettings().getPowerMode();
  414 + }
  415 +
  416 + if (PowerMode.PSM.equals(powerMode) || PowerMode.E_DRX.equals(powerMode)) {
  417 + initAttributes(lwM2MClient);
  418 + TransportProtos.TransportToDeviceActorMsg toDeviceActorMsg = TransportProtos.TransportToDeviceActorMsg
  419 + .newBuilder()
  420 + .setSessionInfo(lwM2MClient.getSession())
  421 + .setSendPendingRPC(TransportProtos.SendPendingRPCMsg.newBuilder().build())
  422 + .build();
  423 + transportService.process(toDeviceActorMsg, TransportServiceCallback.EMPTY);
  424 + }
  425 + }
413 426 }
414 427
415 428 /**
... ... @@ -709,7 +722,6 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl
709 722 this.updateResourcesValue(client, resource, path + "/" + resId);
710 723 });
711 724 }
712   -
713 725 }
714 726
715 727 //TODO: review and optimize the logic to minimize number of the requests to device.
... ... @@ -781,14 +793,14 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl
781 793 OtherConfiguration newLwM2mSettings = newProfile.getClientLwM2mSettings();
782 794 OtherConfiguration oldLwM2mSettings = oldProfile.getClientLwM2mSettings();
783 795 if (!newLwM2mSettings.getFwUpdateStrategy().equals(oldLwM2mSettings.getFwUpdateStrategy())
784   - || (StringUtils.isNotEmpty(newLwM2mSettings.getFwUpdateRecourse()) &&
785   - !newLwM2mSettings.getFwUpdateRecourse().equals(oldLwM2mSettings.getFwUpdateRecourse()))) {
  796 + || (StringUtils.isNotEmpty(newLwM2mSettings.getFwUpdateResource()) &&
  797 + !newLwM2mSettings.getFwUpdateResource().equals(oldLwM2mSettings.getFwUpdateResource()))) {
786 798 clients.forEach(lwM2MClient -> otaService.onCurrentFirmwareStrategyUpdate(lwM2MClient, newLwM2mSettings));
787 799 }
788 800
789 801 if (!newLwM2mSettings.getSwUpdateStrategy().equals(oldLwM2mSettings.getSwUpdateStrategy())
790   - || (StringUtils.isNotEmpty(newLwM2mSettings.getSwUpdateRecourse()) &&
791   - !newLwM2mSettings.getSwUpdateRecourse().equals(oldLwM2mSettings.getSwUpdateRecourse()))) {
  802 + || (StringUtils.isNotEmpty(newLwM2mSettings.getSwUpdateResource()) &&
  803 + !newLwM2mSettings.getSwUpdateResource().equals(oldLwM2mSettings.getSwUpdateResource()))) {
792 804 clients.forEach(lwM2MClient -> otaService.onCurrentSoftwareStrategyUpdate(lwM2MClient, newLwM2mSettings));
793 805 }
794 806 }
... ... @@ -920,16 +932,6 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl
920 932 }
921 933 }
922 934
923   - private TransportProtos.GetOtaPackageRequestMsg createOtaPackageRequestMsg(SessionInfoProto sessionInfo, String nameFwSW) {
924   - return TransportProtos.GetOtaPackageRequestMsg.newBuilder()
925   - .setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
926   - .setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
927   - .setTenantIdMSB(sessionInfo.getTenantIdMSB())
928   - .setTenantIdLSB(sessionInfo.getTenantIdLSB())
929   - .setType(nameFwSW)
930   - .build();
931   - }
932   -
933 935 private Map<String, String> getNamesFromProfileForSharedAttributes(LwM2mClient lwM2MClient) {
934 936 Lwm2mDeviceProfileTransportConfiguration profile = clientContext.getProfile(lwM2MClient.getProfileId());
935 937 return profile.getObserveAttr().getKeyName();
... ...
... ... @@ -50,7 +50,6 @@ import org.thingsboard.server.common.data.TransportPayloadType;
50 50 import org.thingsboard.server.common.data.device.profile.MqttTopics;
51 51 import org.thingsboard.server.common.data.id.OtaPackageId;
52 52 import org.thingsboard.server.common.data.ota.OtaPackageType;
53   -import org.thingsboard.server.common.data.rpc.RpcStatus;
54 53 import org.thingsboard.server.common.msg.EncryptionUtil;
55 54 import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
56 55 import org.thingsboard.server.common.transport.SessionMsgListener;
... ... @@ -820,25 +819,10 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
820 819 .ifPresent(payload -> {
821 820 ChannelFuture channelFuture = deviceSessionCtx.getChannel().writeAndFlush(payload);
822 821 if (rpcRequest.getPersisted()) {
823   - channelFuture.addListener(future -> {
824   - RpcStatus status;
825   - Throwable t = future.cause();
826   - if (t != null) {
827   - log.error("Failed delivering RPC command to device!", t);
828   - status = RpcStatus.FAILED;
829   - } else if (rpcRequest.getOneway()) {
830   - status = RpcStatus.SUCCESSFUL;
831   - } else {
832   - status = RpcStatus.DELIVERED;
833   - }
834   - TransportProtos.ToDevicePersistedRpcResponseMsg msg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder()
835   - .setRequestId(rpcRequest.getRequestId())
836   - .setRequestIdLSB(rpcRequest.getRequestIdLSB())
837   - .setRequestIdMSB(rpcRequest.getRequestIdMSB())
838   - .setStatus(status.name())
839   - .build();
840   - transportService.process(deviceSessionCtx.getSessionInfo(), msg, TransportServiceCallback.EMPTY);
841   - });
  822 + channelFuture.addListener(future ->
  823 + transportService.process(deviceSessionCtx.getSessionInfo(), rpcRequest,
  824 + future.cause() != null, TransportServiceCallback.EMPTY)
  825 + );
842 826 }
843 827 });
844 828 } catch (Exception e) {
... ...
... ... @@ -18,7 +18,6 @@ package org.thingsboard.server.transport.mqtt.session;
18 18 import io.netty.channel.ChannelFuture;
19 19 import lombok.extern.slf4j.Slf4j;
20 20 import org.thingsboard.server.common.data.DeviceProfile;
21   -import org.thingsboard.server.common.data.rpc.RpcStatus;
22 21 import org.thingsboard.server.common.transport.SessionMsgListener;
23 22 import org.thingsboard.server.common.transport.TransportService;
24 23 import org.thingsboard.server.common.transport.TransportServiceCallback;
... ... @@ -102,25 +101,9 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple
102 101 payload -> {
103 102 ChannelFuture channelFuture = parent.writeAndFlush(payload);
104 103 if (request.getPersisted()) {
105   - channelFuture.addListener(future -> {
106   - RpcStatus status;
107   - Throwable t = future.cause();
108   - if (t != null) {
109   - log.error("Failed delivering RPC command to device!", t);
110   - status = RpcStatus.FAILED;
111   - } else if (request.getOneway()) {
112   - status = RpcStatus.SUCCESSFUL;
113   - } else {
114   - status = RpcStatus.DELIVERED;
115   - }
116   - TransportProtos.ToDevicePersistedRpcResponseMsg msg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder()
117   - .setRequestId(request.getRequestId())
118   - .setRequestIdLSB(request.getRequestIdLSB())
119   - .setRequestIdMSB(request.getRequestIdMSB())
120   - .setStatus(status.name())
121   - .build();
122   - transportService.process(getSessionInfo(), msg, TransportServiceCallback.EMPTY);
123   - });
  104 + channelFuture.addListener(future ->
  105 + transportService.process(getSessionInfo(), request, future.cause() != null, TransportServiceCallback.EMPTY)
  106 + );
124 107 }
125 108 }
126 109 );
... ...
... ... @@ -26,7 +26,6 @@ import org.thingsboard.server.common.data.DeviceProfile;
26 26 import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
27 27 import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration;
28 28 import org.thingsboard.server.common.data.id.DeviceId;
29   -import org.thingsboard.server.common.data.rpc.RpcStatus;
30 29 import org.thingsboard.server.common.transport.SessionMsgListener;
31 30 import org.thingsboard.server.common.transport.TransportServiceCallback;
32 31 import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext;
... ... @@ -141,23 +140,9 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
141 140
142 141 @Override
143 142 public void onToDeviceRpcRequest(UUID sessionId, ToDeviceRpcRequestMsg toDeviceRequest) {
144   - log.trace("[{}] Received RPC command to device", sessionId);
145   - snmpTransportContext.getSnmpTransportService().onToDeviceRpcRequest(this, toDeviceRequest);
146   - if (toDeviceRequest.getPersisted()) {
147   - RpcStatus status;
148   - if (toDeviceRequest.getOneway()) {
149   - status = RpcStatus.SUCCESSFUL;
150   - } else {
151   - status = RpcStatus.DELIVERED;
152   - }
153   - TransportProtos.ToDevicePersistedRpcResponseMsg responseMsg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder()
154   - .setRequestId(toDeviceRequest.getRequestId())
155   - .setRequestIdLSB(toDeviceRequest.getRequestIdLSB())
156   - .setRequestIdMSB(toDeviceRequest.getRequestIdMSB())
157   - .setStatus(status.name())
158   - .build();
159   - snmpTransportContext.getTransportService().process(getSessionInfo(), responseMsg, TransportServiceCallback.EMPTY);
160   - }
  143 + log.trace("[{}] Received RPC command to device", sessionId);
  144 + snmpTransportContext.getSnmpTransportService().onToDeviceRpcRequest(this, toDeviceRequest);
  145 + snmpTransportContext.getTransportService().process(getSessionInfo(), toDeviceRequest, false, TransportServiceCallback.EMPTY);
161 146 }
162 147
163 148 @Override
... ...
... ... @@ -46,7 +46,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
46 46 import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg;
47 47 import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg;
48 48 import org.thingsboard.server.gen.transport.TransportProtos.SubscriptionInfoProto;
49   -import org.thingsboard.server.gen.transport.TransportProtos.ToDevicePersistedRpcResponseMsg;
  49 +import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg;
50 50 import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg;
51 51 import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcRequestMsg;
52 52 import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
... ... @@ -109,7 +109,7 @@ public interface TransportService {
109 109
110 110 void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback<Void> callback);
111 111
112   - void process(SessionInfoProto sessionInfo, ToDevicePersistedRpcResponseMsg msg, TransportServiceCallback<Void> callback);
  112 + void process(SessionInfoProto sessionInfo, ToDeviceRpcRequestMsg msg, boolean isFailedRpc, TransportServiceCallback<Void> callback);
113 113
114 114 void process(SessionInfoProto sessionInfo, SubscriptionInfoProto msg, TransportServiceCallback<Void> callback);
115 115
... ...
... ... @@ -16,6 +16,7 @@
16 16 package org.thingsboard.server.common.transport.auth;
17 17
18 18 import lombok.Data;
  19 +import org.thingsboard.server.common.data.device.data.PowerMode;
19 20 import org.thingsboard.server.common.data.id.CustomerId;
20 21 import org.thingsboard.server.common.data.id.DeviceId;
21 22 import org.thingsboard.server.common.data.id.DeviceProfileId;
... ... @@ -32,6 +33,7 @@ public class TransportDeviceInfo implements Serializable {
32 33 private DeviceId deviceId;
33 34 private String deviceName;
34 35 private String deviceType;
  36 + private PowerMode powerMode;
35 37 private String additionalInfo;
36 38
37 39 }
... ...
... ... @@ -34,7 +34,9 @@ import org.thingsboard.server.common.data.DeviceProfile;
34 34 import org.thingsboard.server.common.data.DeviceTransportType;
35 35 import org.thingsboard.server.common.data.EntityType;
36 36 import org.thingsboard.server.common.data.ResourceType;
  37 +import org.thingsboard.server.common.data.StringUtils;
37 38 import org.thingsboard.server.common.data.Tenant;
  39 +import org.thingsboard.server.common.data.device.data.PowerMode;
38 40 import org.thingsboard.server.common.data.id.CustomerId;
39 41 import org.thingsboard.server.common.data.id.DeviceId;
40 42 import org.thingsboard.server.common.data.id.DeviceProfileId;
... ... @@ -42,6 +44,7 @@ import org.thingsboard.server.common.data.id.EntityId;
42 44 import org.thingsboard.server.common.data.id.RuleChainId;
43 45 import org.thingsboard.server.common.data.id.TenantId;
44 46 import org.thingsboard.server.common.data.id.TenantProfileId;
  47 +import org.thingsboard.server.common.data.rpc.RpcStatus;
45 48 import org.thingsboard.server.common.msg.TbMsg;
46 49 import org.thingsboard.server.common.msg.TbMsgMetaData;
47 50 import org.thingsboard.server.common.msg.queue.ServiceQueue;
... ... @@ -439,6 +442,9 @@ public class DefaultTransportService implements TransportService {
439 442 tdi.setAdditionalInfo(di.getAdditionalInfo());
440 443 tdi.setDeviceName(di.getDeviceName());
441 444 tdi.setDeviceType(di.getDeviceType());
  445 + if (StringUtils.isNotEmpty(di.getPowerMode())) {
  446 + tdi.setPowerMode(PowerMode.valueOf(di.getPowerMode()));
  447 + }
442 448 return tdi;
443 449 }
444 450
... ... @@ -558,11 +564,30 @@ public class DefaultTransportService implements TransportService {
558 564 }
559 565
560 566 @Override
561   - public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToDevicePersistedRpcResponseMsg msg, TransportServiceCallback<Void> callback) {
562   - if (checkLimits(sessionInfo, msg, callback)) {
563   - reportActivityInternal(sessionInfo);
564   - sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setPersistedRpcResponseMsg(msg).build(),
565   - new ApiStatsProxyCallback<>(getTenantId(sessionInfo), getCustomerId(sessionInfo), 1, callback));
  567 + public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToDeviceRpcRequestMsg msg, boolean isFailedRpc, TransportServiceCallback<Void> callback) {
  568 + if (msg.getPersisted()) {
  569 + RpcStatus status;
  570 +
  571 + if (isFailedRpc) {
  572 + status = RpcStatus.FAILED;
  573 + } else if (msg.getOneway()) {
  574 + status = RpcStatus.SUCCESSFUL;
  575 + } else {
  576 + status = RpcStatus.DELIVERED;
  577 + }
  578 +
  579 + TransportProtos.ToDevicePersistedRpcResponseMsg responseMsg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder()
  580 + .setRequestId(msg.getRequestId())
  581 + .setRequestIdLSB(msg.getRequestIdLSB())
  582 + .setRequestIdMSB(msg.getRequestIdMSB())
  583 + .setStatus(status.name())
  584 + .build();
  585 +
  586 + if (checkLimits(sessionInfo, responseMsg, callback)) {
  587 + reportActivityInternal(sessionInfo);
  588 + sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setPersistedRpcResponseMsg(responseMsg).build(),
  589 + new ApiStatsProxyCallback<>(getTenantId(sessionInfo), getCustomerId(sessionInfo), 1, TransportServiceCallback.EMPTY));
  590 + }
566 591 }
567 592 }
568 593
... ...
... ... @@ -133,10 +133,10 @@
133 133 </mat-select>
134 134 </mat-form-field>
135 135 <mat-form-field class="mat-block" fxFlex *ngIf="isFwUpdateStrategy">
136   - <mat-label>{{ 'device-profile.lwm2m.fw-update-recourse' | translate }}</mat-label>
137   - <input matInput formControlName="fwUpdateRecourse" required>
138   - <mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateRecourse').hasError('required')">
139   - {{ 'device-profile.lwm2m.fw-update-recourse-required' | translate }}
  136 + <mat-label>{{ 'device-profile.lwm2m.fw-update-resource' | translate }}</mat-label>
  137 + <input matInput formControlName="fwUpdateResource" required>
  138 + <mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateResource').hasError('required')">
  139 + {{ 'device-profile.lwm2m.fw-update-resource-required' | translate }}
140 140 </mat-error>
141 141 </mat-form-field>
142 142 </fieldset>
... ... @@ -150,13 +150,24 @@
150 150 </mat-select>
151 151 </mat-form-field>
152 152 <mat-form-field class="mat-block" fxFlex *ngIf="isSwUpdateStrategy">
153   - <mat-label>{{ 'device-profile.lwm2m.sw-update-recourse' | translate }}</mat-label>
154   - <input matInput formControlName="swUpdateRecourse" required>
155   - <mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateRecourse').hasError('required')">
156   - {{ 'device-profile.lwm2m.sw-update-recourse-required' | translate }}
  153 + <mat-label>{{ 'device-profile.lwm2m.sw-update-resource' | translate }}</mat-label>
  154 + <input matInput formControlName="swUpdateResource" required>
  155 + <mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateResource').hasError('required')">
  156 + {{ 'device-profile.lwm2m.sw-update-resource-required' | translate }}
157 157 </mat-error>
158 158 </mat-form-field>
159 159 </fieldset>
  160 + <fieldset class="fields-group">
  161 + <legend class="group-title" translate>device-profile.power-saving-mode</legend>
  162 + <mat-form-field class="mat-block" fxFlex>
  163 + <mat-label> </mat-label>
  164 + <mat-select formControlName="powerMode">
  165 + <mat-option *ngFor="let powerMod of powerMods" [value]="powerMod">
  166 + {{ powerModeTranslationMap.get(powerMod) | translate}}
  167 + </mat-option>
  168 + </mat-select>
  169 + </mat-form-field>
  170 + </fieldset>
160 171 <!-- <mat-accordion multi="true">-->
161 172 <!-- <div *ngIf="false">-->
162 173 <!-- <mat-expansion-panel>-->
... ...
... ... @@ -30,7 +30,8 @@ import {
30 30 DEFAULT_NOTIF_IF_DESIBLED,
31 31 DEFAULT_SW_UPDATE_RESOURCE,
32 32 getDefaultBootstrapServerSecurityConfig,
33   - getDefaultBootstrapServersSecurityConfig, getDefaultLwM2MServerSecurityConfig,
  33 + getDefaultBootstrapServersSecurityConfig,
  34 + getDefaultLwM2MServerSecurityConfig,
34 35 getDefaultProfileClientLwM2mSettingsConfig,
35 36 getDefaultProfileObserveAttrConfig,
36 37 Instance,
... ... @@ -41,7 +42,10 @@ import {
41 42 ObjectLwM2M,
42 43 OBSERVE,
43 44 OBSERVE_ATTR_TELEMETRY,
44   - RESOURCES, ServerSecurityConfig,
  45 + PowerMode,
  46 + PowerModeTranslationMap,
  47 + RESOURCES,
  48 + ServerSecurityConfig,
45 49 TELEMETRY
46 50 } from './lwm2m-profile-config.models';
47 51 import { DeviceProfileService } from '@core/http/device-profile.service';
... ... @@ -76,6 +80,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
76 80 sortFunction: (key: string, value: object) => object;
77 81 isFwUpdateStrategy: boolean;
78 82 isSwUpdateStrategy: boolean;
  83 + powerMods = Object.values(PowerMode);
  84 + powerModeTranslationMap = PowerModeTranslationMap;
79 85
80 86 get required(): boolean {
81 87 return this.requiredValue;
... ... @@ -109,8 +115,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
109 115 clientOnlyObserveAfterConnect: [1, []],
110 116 fwUpdateStrategy: [1, []],
111 117 swUpdateStrategy: [1, []],
112   - fwUpdateRecourse: [{value: '', disabled: true}, []],
113   - swUpdateRecourse: [{value: '', disabled: true}, []]
  118 + fwUpdateResource: [{value: '', disabled: true}, []],
  119 + swUpdateResource: [{value: '', disabled: true}, []],
  120 + powerMode: [null, Validators.required]
114 121 })
115 122 });
116 123 this.lwm2mDeviceConfigFormGroup = this.fb.group({
... ... @@ -120,12 +127,12 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
120 127 takeUntil(this.destroy$)
121 128 ).subscribe((fwStrategy) => {
122 129 if (fwStrategy === 2) {
123   - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateRecourse').enable({emitEvent: false});
124   - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateRecourse')
  130 + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateResource').enable({emitEvent: false});
  131 + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateResource')
125 132 .patchValue(DEFAULT_FW_UPDATE_RESOURCE, {emitEvent: false});
126 133 this.isFwUpdateStrategy = true;
127 134 } else {
128   - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateRecourse').disable({emitEvent: false});
  135 + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateResource').disable({emitEvent: false});
129 136 this.isFwUpdateStrategy = false;
130 137 }
131 138 this.otaUpdateFwStrategyValidate(true);
... ... @@ -134,13 +141,13 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
134 141 takeUntil(this.destroy$)
135 142 ).subscribe((swStrategy) => {
136 143 if (swStrategy === 2) {
137   - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateRecourse').enable({emitEvent: false});
138   - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateRecourse')
  144 + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateResource').enable({emitEvent: false});
  145 + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateResource')
139 146 .patchValue(DEFAULT_SW_UPDATE_RESOURCE, {emitEvent: false});
140 147 this.isSwUpdateStrategy = true;
141 148 } else {
142 149 this.isSwUpdateStrategy = false;
143   - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateRecourse').disable({emitEvent: false});
  150 + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateResource').disable({emitEvent: false});
144 151 }
145 152 this.otaUpdateSwStrategyValidate(true);
146 153 });
... ... @@ -237,10 +244,10 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
237 244 }
238 245
239 246 private updateWriteValue = (value: ModelValue): void => {
240   - const fwResource = isDefinedAndNotNull(this.configurationValue.clientLwM2mSettings.fwUpdateRecourse) ?
241   - this.configurationValue.clientLwM2mSettings.fwUpdateRecourse : '';
242   - const swResource = isDefinedAndNotNull(this.configurationValue.clientLwM2mSettings.fwUpdateRecourse) ?
243   - this.configurationValue.clientLwM2mSettings.swUpdateRecourse : '';
  247 + const fwResource = isDefinedAndNotNull(this.configurationValue.clientLwM2mSettings.fwUpdateResource) ?
  248 + this.configurationValue.clientLwM2mSettings.fwUpdateResource : '';
  249 + const swResource = isDefinedAndNotNull(this.configurationValue.clientLwM2mSettings.swUpdateResource) ?
  250 + this.configurationValue.clientLwM2mSettings.swUpdateResource : '';
244 251 this.lwm2mDeviceProfileFormGroup.patchValue({
245 252 objectIds: value,
246 253 observeAttrTelemetry: this.getObserveAttrTelemetryObjects(value.objectsList),
... ... @@ -249,13 +256,14 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
249 256 clientOnlyObserveAfterConnect: this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect,
250 257 fwUpdateStrategy: this.configurationValue.clientLwM2mSettings.fwUpdateStrategy || 1,
251 258 swUpdateStrategy: this.configurationValue.clientLwM2mSettings.swUpdateStrategy || 1,
252   - fwUpdateRecourse: fwResource,
253   - swUpdateRecourse: swResource
  259 + fwUpdateResource: fwResource,
  260 + swUpdateResource: swResource,
  261 + powerMode: this.configurationValue.clientLwM2mSettings.powerMode
254 262 }
255 263 },
256 264 {emitEvent: false});
257   - this.configurationValue.clientLwM2mSettings.fwUpdateRecourse = fwResource;
258   - this.configurationValue.clientLwM2mSettings.swUpdateRecourse = swResource;
  265 + this.configurationValue.clientLwM2mSettings.fwUpdateResource = fwResource;
  266 + this.configurationValue.clientLwM2mSettings.swUpdateResource = swResource;
259 267 this.isFwUpdateStrategy = this.configurationValue.clientLwM2mSettings.fwUpdateStrategy === 2;
260 268 this.isSwUpdateStrategy = this.configurationValue.clientLwM2mSettings.swUpdateStrategy === 2;
261 269 this.otaUpdateSwStrategyValidate();
... ... @@ -561,20 +569,20 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
561 569
562 570 private otaUpdateFwStrategyValidate(updated = false): void {
563 571 if (this.isFwUpdateStrategy) {
564   - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateRecourse').setValidators([Validators.required]);
  572 + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateResource').setValidators([Validators.required]);
565 573 } else {
566   - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateRecourse').clearValidators();
  574 + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateResource').clearValidators();
567 575 }
568   - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateRecourse').updateValueAndValidity({emitEvent: updated});
  576 + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateResource').updateValueAndValidity({emitEvent: updated});
569 577 }
570 578
571 579 private otaUpdateSwStrategyValidate(updated = false): void {
572 580 if (this.isSwUpdateStrategy) {
573   - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateRecourse').setValidators([Validators.required]);
  581 + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateResource').setValidators([Validators.required]);
574 582 } else {
575   - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateRecourse').clearValidators();
  583 + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateResource').clearValidators();
576 584 }
577   - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateRecourse').updateValueAndValidity({emitEvent: updated});
  585 + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateResource').updateValueAndValidity({emitEvent: updated});
578 586 }
579 587
580 588 }
... ...
... ... @@ -115,6 +115,20 @@ export const securityConfigModeNames = new Map<securityConfigMode, string>(
115 115 ]
116 116 );
117 117
  118 +export enum PowerMode {
  119 + PSM = 'PSM',
  120 + DRX = 'DRX',
  121 + E_DRX = 'E_DRX'
  122 +}
  123 +
  124 +export const PowerModeTranslationMap = new Map<PowerMode, string>(
  125 + [
  126 + [PowerMode.PSM, 'device-profile.power-saving-mode-type.psm'],
  127 + [PowerMode.DRX, 'device-profile.power-saving-mode-type.drx'],
  128 + [PowerMode.E_DRX, 'device-profile.power-saving-mode-type.edrx']
  129 + ]
  130 +);
  131 +
118 132 export interface ModelValue {
119 133 objectIds: string[];
120 134 objectsList: ObjectLwM2M[];
... ... @@ -160,8 +174,9 @@ export interface ClientLwM2mSettings {
160 174 clientOnlyObserveAfterConnect: number;
161 175 fwUpdateStrategy: number;
162 176 swUpdateStrategy: number;
163   - fwUpdateRecourse: string;
164   - swUpdateRecourse: string;
  177 + fwUpdateResource: string;
  178 + swUpdateResource: string;
  179 + powerMode: PowerMode;
165 180 }
166 181
167 182 export interface ObservableAttributes {
... ... @@ -216,8 +231,9 @@ export function getDefaultProfileClientLwM2mSettingsConfig(): ClientLwM2mSetting
216 231 clientOnlyObserveAfterConnect: 1,
217 232 fwUpdateStrategy: 1,
218 233 swUpdateStrategy: 1,
219   - fwUpdateRecourse: DEFAULT_FW_UPDATE_RESOURCE,
220   - swUpdateRecourse: DEFAULT_SW_UPDATE_RESOURCE
  234 + fwUpdateResource: DEFAULT_FW_UPDATE_RESOURCE,
  235 + swUpdateResource: DEFAULT_SW_UPDATE_RESOURCE,
  236 + powerMode: PowerMode.DRX
221 237 };
222 238 }
223 239
... ...
... ... @@ -29,12 +29,12 @@
29 29 formControlName="configuration">
30 30 </tb-mqtt-device-transport-configuration>
31 31 </ng-template>
32   - <!--ng-template [ngSwitchCase]="deviceTransportType.LWM2M">
  32 + <ng-template [ngSwitchCase]="deviceTransportType.LWM2M">
33 33 <tb-lwm2m-device-transport-configuration
34 34 [required]="required"
35 35 formControlName="configuration">
36 36 </tb-lwm2m-device-transport-configuration>
37   - </ng-template-->
  37 + </ng-template>
38 38 <ng-template [ngSwitchCase]="deviceTransportType.COAP">
39 39 <tb-coap-device-transport-configuration
40 40 [required]="required"
... ...
... ... @@ -16,9 +16,13 @@
16 16
17 17 -->
18 18 <form [formGroup]="lwm2mDeviceTransportConfigurationFormGroup" style="padding-bottom: 16px;">
19   - <!--tb-json-object-edit
20   - [required]="required"
21   - label="{{ 'device-profile.transport-type-lwm2m' | translate }}"
22   - formControlName="configuration">
23   - </tb-json-object-edit-->
  19 + <mat-form-field class="mat-block" fxFlex>
  20 + <mat-label translate>device-profile.power-saving-mode</mat-label>
  21 + <mat-select formControlName="powerMode">
  22 + <mat-option [value]="null">{{ "device-profile.power-saving-mode-type.default" | translate }}</mat-option>
  23 + <mat-option *ngFor="let powerMod of powerMods" [value]="powerMod">
  24 + {{ powerModeTranslationMap.get(powerMod) | translate }}
  25 + </mat-option>
  26 + </mat-select>
  27 + </mat-form-field>
24 28 </form>
... ...
... ... @@ -23,6 +23,7 @@ import {
23 23 DeviceTransportConfiguration,
24 24 DeviceTransportType, Lwm2mDeviceTransportConfiguration
25 25 } from '@shared/models/device.models';
  26 +import {PowerMode, PowerModeTranslationMap} from "@home/components/profile/device/lwm2m/lwm2m-profile-config.models";
26 27
27 28 @Component({
28 29 selector: 'tb-lwm2m-device-transport-configuration',
... ... @@ -37,6 +38,8 @@ import {
37 38 export class Lwm2mDeviceTransportConfigurationComponent implements ControlValueAccessor, OnInit {
38 39
39 40 lwm2mDeviceTransportConfigurationFormGroup: FormGroup;
  41 + powerMods = Object.values(PowerMode);
  42 + powerModeTranslationMap = PowerModeTranslationMap;
40 43
41 44 private requiredValue: boolean;
42 45 get required(): boolean {
... ... @@ -65,7 +68,7 @@ export class Lwm2mDeviceTransportConfigurationComponent implements ControlValueA
65 68
66 69 ngOnInit() {
67 70 this.lwm2mDeviceTransportConfigurationFormGroup = this.fb.group({
68   - configuration: [null, Validators.required]
  71 + powerMode: [null]
69 72 });
70 73 this.lwm2mDeviceTransportConfigurationFormGroup.valueChanges.subscribe(() => {
71 74 this.updateModel();
... ... @@ -82,13 +85,13 @@ export class Lwm2mDeviceTransportConfigurationComponent implements ControlValueA
82 85 }
83 86
84 87 writeValue(value: Lwm2mDeviceTransportConfiguration | null): void {
85   - this.lwm2mDeviceTransportConfigurationFormGroup.patchValue({configuration: value}, {emitEvent: false});
  88 + this.lwm2mDeviceTransportConfigurationFormGroup.patchValue(value, {emitEvent: false});
86 89 }
87 90
88 91 private updateModel() {
89 92 let configuration: DeviceTransportConfiguration = null;
90 93 if (this.lwm2mDeviceTransportConfigurationFormGroup.valid) {
91   - configuration = this.lwm2mDeviceTransportConfigurationFormGroup.getRawValue().configuration;
  94 + configuration = this.lwm2mDeviceTransportConfigurationFormGroup.getRawValue();
92 95 // configuration.type = DeviceTransportType.LWM2M;
93 96 }
94 97 this.propagateChange(configuration);
... ...
... ... @@ -200,7 +200,7 @@ export const deviceTransportTypeConfigurationInfoMap = new Map<DeviceTransportTy
200 200 DeviceTransportType.LWM2M,
201 201 {
202 202 hasProfileConfiguration: true,
203   - hasDeviceConfiguration: false,
  203 + hasDeviceConfiguration: true,
204 204 }
205 205 ],
206 206 [
... ...
... ... @@ -1213,6 +1213,13 @@
1213 1213 "export-failed-error": "Unable to export device profile: {{error}}",
1214 1214 "device-profile-file": "Device profile file",
1215 1215 "invalid-device-profile-file-error": "Unable to import device profile: Invalid device profile data structure.",
  1216 + "power-saving-mode": "Power Saving Mode",
  1217 + "power-saving-mode-type": {
  1218 + "default": "Use device profile power saving mode",
  1219 + "psm": "Power Saving Mode (PSM)",
  1220 + "drx": "Discontinuous Reception (DRX)",
  1221 + "edrx": "Extended Discontinuous Reception (eDRX)"
  1222 + },
1216 1223 "lwm2m": {
1217 1224 "object-list": "Object list",
1218 1225 "object-list-empty": "No objects selected.",
... ... @@ -1313,10 +1320,10 @@
1313 1320 "sw-update-strategy": "Software update strategy",
1314 1321 "sw-update-strategy-package": "Push binary file using Object 9 and Resource 2 (Package)",
1315 1322 "sw-update-strategy-package-uri": "Auto-generate unique CoAP URL to download the package and push software update using Object 9 and Resource 3 (Package URI)",
1316   - "fw-update-recourse": "Firmware update CoAP recourse",
1317   - "fw-update-recourse-required": "Firmware update CoAP recourse is required.",
1318   - "sw-update-recourse": "Software update CoAP recourse",
1319   - "sw-update-recourse-required": "Software update CoAP recourse is required.",
  1323 + "fw-update-resource": "Firmware update CoAP resource",
  1324 + "fw-update-resource-required": "Firmware update CoAP resource is required.",
  1325 + "sw-update-resource": "Software update CoAP resource",
  1326 + "sw-update-resource-required": "Software update CoAP resource is required.",
1320 1327 "config-json-tab": "Json Config Profile Device",
1321 1328 "attributes-name": {
1322 1329 "min-period": "Minimum period",
... ...