Commit a665b2cbe631dc87ed128b2182cfbc25ddff03c7

Authored by YevhenBondarenko
1 parent ba507e99

added power mode to lwm2m-device-profile-transport-configuration

Showing 25 changed files with 186 additions and 186 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,6 +461,13 @@ public class DefaultTransportApiService implements TransportApiService {
459 461 }
460 462
461 463 private DeviceInfoProto getDeviceInfoProto(Device device) throws JsonProcessingException {
  464 + PowerMode powerMode = null;
  465 + switch (device.getDeviceData().getTransportConfiguration().getType()) {
  466 + case LWM2M:
  467 + powerMode = ((Lwm2mDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration()).getPowerMode();
  468 + break;
  469 + }
  470 +
462 471 return DeviceInfoProto.newBuilder()
463 472 .setTenantIdMSB(device.getTenantId().getId().getMostSignificantBits())
464 473 .setTenantIdLSB(device.getTenantId().getId().getLeastSignificantBits())
... ... @@ -471,6 +480,7 @@ public class DefaultTransportApiService implements TransportApiService {
471 480 .setDeviceProfileIdMSB(device.getDeviceProfileId().getId().getMostSignificantBits())
472 481 .setDeviceProfileIdLSB(device.getDeviceProfileId().getId().getLeastSignificantBits())
473 482 .setAdditionalInfo(mapper.writeValueAsString(device.getAdditionalInfo()))
  483 + .setPowerMode(powerMode != null ? powerMode.name() : null)
474 484 .build();
475 485 }
476 486
... ...
... ... @@ -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
... ...
... ... @@ -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;
... ... @@ -515,23 +514,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
515 514 exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR);
516 515 successful = false;
517 516 }
518   - if (msg.getPersisted()) {
519   - RpcStatus status;
520   - if (!successful) {
521   - status = RpcStatus.FAILED;
522   - } else if (msg.getOneway()) {
523   - status = RpcStatus.SUCCESSFUL;
524   - } else {
525   - status = RpcStatus.DELIVERED;
526   - }
527   - TransportProtos.ToDevicePersistedRpcResponseMsg responseMsg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder()
528   - .setRequestId(msg.getRequestId())
529   - .setRequestIdLSB(msg.getRequestIdLSB())
530   - .setRequestIdMSB(msg.getRequestIdMSB())
531   - .setStatus(status.name())
532   - .build();
533   - coapTransportResource.transportService.process(sessionInfo, responseMsg, TransportServiceCallback.EMPTY);
534   - }
  517 + coapTransportResource.transportService.process(sessionInfo, msg, !successful, TransportServiceCallback.EMPTY);
535 518 }
536 519
537 520 @Override
... ...
... ... @@ -406,21 +406,7 @@ public class DeviceApiController implements TbTransportService {
406 406 @Override
407 407 public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg msg) {
408 408 responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg, true).toString(), HttpStatus.OK));
409   - if (msg.getPersisted()) {
410   - RpcStatus status;
411   - if (msg.getOneway()) {
412   - status = RpcStatus.SUCCESSFUL;
413   - } else {
414   - status = RpcStatus.DELIVERED;
415   - }
416   - TransportProtos.ToDevicePersistedRpcResponseMsg responseMsg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder()
417   - .setRequestId(msg.getRequestId())
418   - .setRequestIdLSB(msg.getRequestIdLSB())
419   - .setRequestIdMSB(msg.getRequestIdMSB())
420   - .setStatus(status.name())
421   - .build();
422   - transportService.process(sessionInfo, responseMsg, TransportServiceCallback.EMPTY);
423   - }
  409 + transportService.process(sessionInfo, msg, false, TransportServiceCallback.EMPTY);
424 410 }
425 411
426 412 @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;
... ...
... ... @@ -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;
... ... @@ -58,7 +57,7 @@ public class LwM2mSessionMsgListener implements GenericFutureListener<Future<? s
58 57 @Override
59 58 public void onAttributeUpdate(AttributeUpdateNotificationMsg attributeUpdateNotification) {
60 59 this.attributesService.onAttributesUpdate(attributeUpdateNotification, this.sessionInfo);
61   - }
  60 + }
62 61
63 62 @Override
64 63 public void onRemoteSessionCloseCommand(UUID sessionId, SessionCloseNotificationProto sessionCloseNotification) {
... ... @@ -83,21 +82,7 @@ public class LwM2mSessionMsgListener implements GenericFutureListener<Future<? s
83 82 @Override
84 83 public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest) {
85 84 this.rpcHandler.onToDeviceRpcRequest(toDeviceRequest, this.sessionInfo);
86   - if (toDeviceRequest.getPersisted()) {
87   - RpcStatus status;
88   - if (toDeviceRequest.getOneway()) {
89   - status = RpcStatus.SUCCESSFUL;
90   - } else {
91   - status = RpcStatus.DELIVERED;
92   - }
93   - TransportProtos.ToDevicePersistedRpcResponseMsg responseMsg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder()
94   - .setRequestId(toDeviceRequest.getRequestId())
95   - .setRequestIdLSB(toDeviceRequest.getRequestIdLSB())
96   - .setRequestIdMSB(toDeviceRequest.getRequestIdMSB())
97   - .setStatus(status.name())
98   - .build();
99   - transportService.process(sessionInfo, responseMsg, TransportServiceCallback.EMPTY);
100   - }
  85 + transportService.process(sessionInfo, toDeviceRequest, false, TransportServiceCallback.EMPTY);
101 86 }
102 87
103 88 @Override
... ...
  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) {
... ...
... ... @@ -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,27 @@ 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 + Lwm2mDeviceProfileTransportConfiguration deviceProfile = clientContext.getProfile(lwM2MClient.getProfileId());
  411 +
  412 + PowerMode powerMode = lwM2MClient.getPowerMode();
  413 + if (powerMode == null) {
  414 + powerMode = deviceProfile.getClientLwM2mSettings().getPowerMode();
  415 + }
  416 +
  417 + if (powerMode.equals(PowerMode.PSM) || powerMode.equals(PowerMode.E_DRX)) {
  418 + initAttributes(lwM2MClient);
  419 + TransportProtos.TransportToDeviceActorMsg toDeviceActorMsg = TransportProtos.TransportToDeviceActorMsg
  420 + .newBuilder()
  421 + .setSessionInfo(lwM2MClient.getSession())
  422 + .setSendPendingRPC(TransportProtos.SendPendingRPCMsg.newBuilder().build())
  423 + .build();
  424 + transportService.process(toDeviceActorMsg, TransportServiceCallback.EMPTY);
  425 + }
  426 + }
413 427 }
414 428
415 429 /**
... ... @@ -709,7 +723,6 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl
709 723 this.updateResourcesValue(client, resource, path + "/" + resId);
710 724 });
711 725 }
712   -
713 726 }
714 727
715 728 //TODO: review and optimize the logic to minimize number of the requests to device.
... ... @@ -920,16 +933,6 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl
920 933 }
921 934 }
922 935
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 936 private Map<String, String> getNamesFromProfileForSharedAttributes(LwM2mClient lwM2MClient) {
934 937 Lwm2mDeviceProfileTransportConfiguration profile = clientContext.getProfile(lwM2MClient.getProfileId());
935 938 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;
... ... @@ -819,25 +818,10 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
819 818 .ifPresent(payload -> {
820 819 ChannelFuture channelFuture = deviceSessionCtx.getChannel().writeAndFlush(payload);
821 820 if (rpcRequest.getPersisted()) {
822   - channelFuture.addListener(future -> {
823   - RpcStatus status;
824   - Throwable t = future.cause();
825   - if (t != null) {
826   - log.error("Failed delivering RPC command to device!", t);
827   - status = RpcStatus.FAILED;
828   - } else if (rpcRequest.getOneway()) {
829   - status = RpcStatus.SUCCESSFUL;
830   - } else {
831   - status = RpcStatus.DELIVERED;
832   - }
833   - TransportProtos.ToDevicePersistedRpcResponseMsg msg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder()
834   - .setRequestId(rpcRequest.getRequestId())
835   - .setRequestIdLSB(rpcRequest.getRequestIdLSB())
836   - .setRequestIdMSB(rpcRequest.getRequestIdMSB())
837   - .setStatus(status.name())
838   - .build();
839   - transportService.process(deviceSessionCtx.getSessionInfo(), msg, TransportServiceCallback.EMPTY);
840   - });
  821 + channelFuture.addListener(future ->
  822 + transportService.process(deviceSessionCtx.getSessionInfo(), rpcRequest,
  823 + future.cause() != null, TransportServiceCallback.EMPTY)
  824 + );
841 825 }
842 826 });
843 827 } 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;
... ... @@ -100,25 +99,9 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple
100 99 payload -> {
101 100 ChannelFuture channelFuture = parent.writeAndFlush(payload);
102 101 if (request.getPersisted()) {
103   - channelFuture.addListener(future -> {
104   - RpcStatus status;
105   - Throwable t = future.cause();
106   - if (t != null) {
107   - log.error("Failed delivering RPC command to device!", t);
108   - status = RpcStatus.FAILED;
109   - } else if (request.getOneway()) {
110   - status = RpcStatus.SUCCESSFUL;
111   - } else {
112   - status = RpcStatus.DELIVERED;
113   - }
114   - TransportProtos.ToDevicePersistedRpcResponseMsg msg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder()
115   - .setRequestId(request.getRequestId())
116   - .setRequestIdLSB(request.getRequestIdLSB())
117   - .setRequestIdMSB(request.getRequestIdMSB())
118   - .setStatus(status.name())
119   - .build();
120   - transportService.process(getSessionInfo(), msg, TransportServiceCallback.EMPTY);
121   - });
  102 + channelFuture.addListener(future ->
  103 + transportService.process(getSessionInfo(), request, future.cause() != null, TransportServiceCallback.EMPTY)
  104 + );
122 105 }
123 106 }
124 107 );
... ...
... ... @@ -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;
... ... @@ -140,22 +139,8 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
140 139
141 140 @Override
142 141 public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest) {
143   - snmpTransportContext.getSnmpTransportService().onToDeviceRpcRequest(this, toDeviceRequest);
144   - if (toDeviceRequest.getPersisted()) {
145   - RpcStatus status;
146   - if (toDeviceRequest.getOneway()) {
147   - status = RpcStatus.SUCCESSFUL;
148   - } else {
149   - status = RpcStatus.DELIVERED;
150   - }
151   - TransportProtos.ToDevicePersistedRpcResponseMsg responseMsg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder()
152   - .setRequestId(toDeviceRequest.getRequestId())
153   - .setRequestIdLSB(toDeviceRequest.getRequestIdLSB())
154   - .setRequestIdMSB(toDeviceRequest.getRequestIdMSB())
155   - .setStatus(status.name())
156   - .build();
157   - snmpTransportContext.getTransportService().process(getSessionInfo(), responseMsg, TransportServiceCallback.EMPTY);
158   - }
  142 + snmpTransportContext.getSnmpTransportService().onToDeviceRpcRequest(this, toDeviceRequest);
  143 + snmpTransportContext.getTransportService().process(getSessionInfo(), toDeviceRequest, false, TransportServiceCallback.EMPTY);
159 144 }
160 145
161 146 @Override
... ...
... ... @@ -21,7 +21,6 @@ import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGateway
21 21 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
22 22 import org.thingsboard.server.common.transport.service.SessionMetaData;
23 23 import org.thingsboard.server.gen.transport.TransportProtos;
24   -import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
25 24 import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg;
26 25 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
27 26 import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceCredentialsRequestMsg;
... ... @@ -30,9 +29,9 @@ import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceRequestMsg;
30 29 import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceResponseMsg;
31 30 import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileRequestMsg;
32 31 import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileResponseMsg;
  32 +import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg;
33 33 import org.thingsboard.server.gen.transport.TransportProtos.GetOtaPackageRequestMsg;
34 34 import org.thingsboard.server.gen.transport.TransportProtos.GetOtaPackageResponseMsg;
35   -import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg;
36 35 import org.thingsboard.server.gen.transport.TransportProtos.GetResourceRequestMsg;
37 36 import org.thingsboard.server.gen.transport.TransportProtos.GetResourceResponseMsg;
38 37 import org.thingsboard.server.gen.transport.TransportProtos.GetSnmpDevicesRequestMsg;
... ... @@ -50,8 +49,8 @@ import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg;
50 49 import org.thingsboard.server.gen.transport.TransportProtos.SubscriptionInfoProto;
51 50 import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg;
52 51 import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcRequestMsg;
  52 +import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
53 53 import org.thingsboard.server.gen.transport.TransportProtos.ValidateBasicMqttCredRequestMsg;
54   -import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
55 54 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg;
56 55 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg;
57 56 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg;
... ... @@ -110,7 +109,7 @@ public interface TransportService {
110 109
111 110 void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback<Void> callback);
112 111
113   - void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToDevicePersistedRpcResponseMsg msg, TransportServiceCallback<Void> callback);
  112 + void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToDeviceRpcRequestMsg msg, boolean isFailedRpc, TransportServiceCallback<Void> callback);
114 113
115 114 void process(SessionInfoProto sessionInfo, SubscriptionInfoProto msg, TransportServiceCallback<Void> callback);
116 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;
... ... @@ -30,6 +31,7 @@ public class TransportDeviceInfo {
30 31 private DeviceId deviceId;
31 32 private String deviceName;
32 33 private String deviceType;
  34 + private PowerMode powerMode;
33 35 private String additionalInfo;
34 36
35 37 }
... ...
... ... @@ -35,6 +35,7 @@ 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 37 import org.thingsboard.server.common.data.Tenant;
  38 +import org.thingsboard.server.common.data.device.data.PowerMode;
38 39 import org.thingsboard.server.common.data.id.CustomerId;
39 40 import org.thingsboard.server.common.data.id.DeviceId;
40 41 import org.thingsboard.server.common.data.id.DeviceProfileId;
... ... @@ -42,6 +43,7 @@ import org.thingsboard.server.common.data.id.EntityId;
42 43 import org.thingsboard.server.common.data.id.RuleChainId;
43 44 import org.thingsboard.server.common.data.id.TenantId;
44 45 import org.thingsboard.server.common.data.id.TenantProfileId;
  46 +import org.thingsboard.server.common.data.rpc.RpcStatus;
45 47 import org.thingsboard.server.common.msg.TbMsg;
46 48 import org.thingsboard.server.common.msg.TbMsgMetaData;
47 49 import org.thingsboard.server.common.msg.queue.ServiceQueue;
... ... @@ -439,6 +441,9 @@ public class DefaultTransportService implements TransportService {
439 441 tdi.setAdditionalInfo(di.getAdditionalInfo());
440 442 tdi.setDeviceName(di.getDeviceName());
441 443 tdi.setDeviceType(di.getDeviceType());
  444 + if (di.getPowerMode() != null) {
  445 + tdi.setPowerMode(PowerMode.valueOf(di.getPowerMode()));
  446 + }
442 447 return tdi;
443 448 }
444 449
... ... @@ -558,11 +563,30 @@ public class DefaultTransportService implements TransportService {
558 563 }
559 564
560 565 @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));
  566 + public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToDeviceRpcRequestMsg msg, boolean isFailedRpc, TransportServiceCallback<Void> callback) {
  567 + if (msg.getPersisted()) {
  568 + RpcStatus status;
  569 +
  570 + if (isFailedRpc) {
  571 + status = RpcStatus.FAILED;
  572 + } else if (msg.getOneway()) {
  573 + status = RpcStatus.SUCCESSFUL;
  574 + } else {
  575 + status = RpcStatus.DELIVERED;
  576 + }
  577 +
  578 + TransportProtos.ToDevicePersistedRpcResponseMsg responseMsg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder()
  579 + .setRequestId(msg.getRequestId())
  580 + .setRequestIdLSB(msg.getRequestIdLSB())
  581 + .setRequestIdMSB(msg.getRequestIdMSB())
  582 + .setStatus(status.name())
  583 + .build();
  584 +
  585 + if (checkLimits(sessionInfo, responseMsg, callback)) {
  586 + reportActivityInternal(sessionInfo);
  587 + sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setPersistedRpcResponseMsg(responseMsg).build(),
  588 + new ApiStatsProxyCallback<>(getTenantId(sessionInfo), getCustomerId(sessionInfo), 1, TransportServiceCallback.EMPTY));
  589 + }
566 590 }
567 591 }
568 592
... ...
... ... @@ -151,13 +151,12 @@
151 151 </mat-form-field>
152 152 </fieldset>
153 153 <fieldset class="fields-group">
154   - <legend class="group-title" translate>device-profile.lwm2m.power-mode</legend>
  154 + <legend class="group-title" translate>device-profile.power-saving-mode</legend>
155 155 <mat-form-field class="mat-block" fxFlex>
156 156 <mat-label> </mat-label>
157 157 <mat-select formControlName="powerMode">
158   - <mat-option *ngFor="let powerMode of powerModeLwM2MTypes"
159   - [value]="powerMode">
160   - {{ powerModeLwM2MNamesMap.get(powerModeLwM2MType[powerMode]) }}
  158 + <mat-option *ngFor="let powerMod of powerMods" [value]="powerMod">
  159 + {{ powerModeTranslationMap.get(powerMod) | translate}}
161 160 </mat-option>
162 161 </mat-select>
163 162 </mat-form-field>
... ...
... ... @@ -32,7 +32,7 @@ import {
32 32 ModelValue,
33 33 ObjectLwM2M,
34 34 OBSERVE,
35   - OBSERVE_ATTR_TELEMETRY, powerMode, powerModeNames,
  35 + OBSERVE_ATTR_TELEMETRY, PowerMode, PowerModeTranslationMap,
36 36 RESOURCES,
37 37 TELEMETRY
38 38 } from './lwm2m-profile-config.models';
... ... @@ -72,9 +72,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
72 72 sortFunction: (key: string, value: object) => object;
73 73 isFwUpdateStrategy: boolean;
74 74 isSwUpdateStrategy: boolean;
75   - powerModeLwM2MType = powerMode;
76   - powerModeLwM2MTypes = Object.keys(powerMode);
77   - powerModeLwM2MNamesMap = powerModeNames;
  75 + powerMods = Object.values(PowerMode);
  76 + powerModeTranslationMap = PowerModeTranslationMap;
78 77
79 78 get required(): boolean {
80 79 return this.requiredValue;
... ... @@ -208,7 +207,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
208 207 private updateWriteValue = (value: ModelValue): void => {
209 208 const fwResource = isDefinedAndNotNull(this.configurationValue.clientLwM2mSettings.fwUpdateResource) ?
210 209 this.configurationValue.clientLwM2mSettings.fwUpdateResource : '';
211   - const swResource = isDefinedAndNotNull(this.configurationValue.clientLwM2mSettings.fwUpdateResource) ?
  210 + const swResource = isDefinedAndNotNull(this.configurationValue.clientLwM2mSettings.swUpdateResource) ?
212 211 this.configurationValue.clientLwM2mSettings.swUpdateResource : '';
213 212 this.lwm2mDeviceProfileFormGroup.patchValue({
214 213 objectIds: value,
... ...
... ... @@ -128,17 +128,17 @@ export const securityConfigModeNames = new Map<securityConfigMode, string>(
128 128 ]
129 129 );
130 130
131   -export enum powerMode {
  131 +export enum PowerMode {
132 132 PSM = 'PSM',
133 133 DRX = 'DRX',
134 134 E_DRX = 'E_DRX'
135 135 }
136 136
137   -export const powerModeNames = new Map<powerMode, string>(
  137 +export const PowerModeTranslationMap = new Map<PowerMode, string>(
138 138 [
139   - [powerMode.PSM, 'Power Saving Mode (PSM)'],
140   - [powerMode.DRX, 'Discontinuous Reception (DRX)'],
141   - [powerMode.E_DRX, 'Extended Discontinuous Reception (eDRX)']
  139 + [PowerMode.PSM, 'device-profile.power-saving-mode-type.psm'],
  140 + [PowerMode.DRX, 'device-profile.power-saving-mode-type.drx'],
  141 + [PowerMode.E_DRX, 'device-profile.power-saving-mode-type.edrx']
142 142 ]
143 143 );
144 144
... ... @@ -187,7 +187,7 @@ export interface ClientLwM2mSettings {
187 187 swUpdateStrategy: number;
188 188 fwUpdateResource: string;
189 189 swUpdateResource: string;
190   - powerMode: powerMode;
  190 + powerMode: PowerMode;
191 191 }
192 192
193 193 export interface ObservableAttributes {
... ... @@ -260,7 +260,7 @@ function getDefaultProfileClientLwM2mSettingsConfig(): ClientLwM2mSettings {
260 260 swUpdateStrategy: 1,
261 261 fwUpdateResource: DEFAULT_FW_UPDATE_RESOURCE,
262 262 swUpdateResource: DEFAULT_SW_UPDATE_RESOURCE,
263   - powerMode: powerMode.DRX
  263 + powerMode: PowerMode.DRX
264 264 };
265 265 }
266 266
... ...
... ... @@ -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.",
... ...