Lwm2m Fix bug review 1-10
This commit is contained in:
		
							parent
							
								
									9651b93574
								
							
						
					
					
						commit
						f86a30a8ed
					
				@ -126,7 +126,6 @@ import org.thingsboard.server.service.component.ComponentDiscoveryService;
 | 
			
		||||
import org.thingsboard.server.service.edge.EdgeLicenseService;
 | 
			
		||||
import org.thingsboard.server.service.edge.EdgeNotificationService;
 | 
			
		||||
import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
 | 
			
		||||
import org.thingsboard.server.service.lwm2m.LwM2MServiceImpl;
 | 
			
		||||
import org.thingsboard.server.service.ota.OtaPackageStateService;
 | 
			
		||||
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
 | 
			
		||||
import org.thingsboard.server.service.resource.TbResourceService;
 | 
			
		||||
@ -185,9 +184,6 @@ public abstract class BaseController {
 | 
			
		||||
    @Autowired
 | 
			
		||||
    protected DeviceProfileService deviceProfileService;
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    protected LwM2MServiceImpl lwM2MService;
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    protected AssetService assetService;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,7 @@ package org.thingsboard.server.controller;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.databind.ObjectMapper;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
import org.springframework.security.access.prepost.PreAuthorize;
 | 
			
		||||
import org.springframework.web.bind.annotation.PathVariable;
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestBody;
 | 
			
		||||
@ -31,6 +32,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException;
 | 
			
		||||
import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig;
 | 
			
		||||
import org.thingsboard.server.common.data.security.DeviceCredentials;
 | 
			
		||||
import org.thingsboard.server.queue.util.TbCoreComponent;
 | 
			
		||||
import org.thingsboard.server.service.lwm2m.LwM2MServiceImpl;
 | 
			
		||||
import org.thingsboard.server.service.security.permission.Resource;
 | 
			
		||||
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
@ -41,6 +43,9 @@ import java.util.Map;
 | 
			
		||||
@RequestMapping("/api")
 | 
			
		||||
public class Lwm2mController extends BaseController {
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    protected LwM2MServiceImpl lwM2MService;
 | 
			
		||||
 | 
			
		||||
    @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
 | 
			
		||||
    @RequestMapping(value = "/lwm2m/deviceProfile/bootstrap/{isBootstrapServer}", method = RequestMethod.GET)
 | 
			
		||||
    @ResponseBody
 | 
			
		||||
 | 
			
		||||
@ -693,6 +693,8 @@ transport:
 | 
			
		||||
    ota_pool_size: "${LWM2M_OTA_POOL_SIZE:10}"
 | 
			
		||||
    clean_period_in_sec: "${LWM2M_CLEAN_PERIOD_IN_SEC:2}"
 | 
			
		||||
    log_max_length: "${LWM2M_LOG_MAX_LENGTH:1024}"
 | 
			
		||||
    psm_activity_timer: "${LWM2M_PSM_ACTIVITY_TIMER:10000}"
 | 
			
		||||
    paging_transmission_window: "${LWM2M_PAGING_TRANSMISSION_WINDOW:10000}"
 | 
			
		||||
    # Use redis for Security and Registration stores
 | 
			
		||||
    redis.enabled: "${LWM2M_REDIS_ENABLED:false}"
 | 
			
		||||
  snmp:
 | 
			
		||||
 | 
			
		||||
@ -83,7 +83,7 @@ public class FwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public WriteResponse write(ServerIdentity identity, boolean replace, int resourceId, LwM2mResource value) {
 | 
			
		||||
        log.warn("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceId);
 | 
			
		||||
        log.info("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceId);
 | 
			
		||||
 | 
			
		||||
        switch (resourceId) {
 | 
			
		||||
            case 0:
 | 
			
		||||
 | 
			
		||||
@ -83,7 +83,7 @@ public class SwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public WriteResponse write(ServerIdentity identity, boolean replace, int resourceId, LwM2mResource value) {
 | 
			
		||||
        log.warn("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceId);
 | 
			
		||||
        log.info("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceId);
 | 
			
		||||
 | 
			
		||||
        switch (resourceId) {
 | 
			
		||||
            case 2:
 | 
			
		||||
 | 
			
		||||
@ -15,9 +15,6 @@
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.common.data.security;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.core.JsonProcessingException;
 | 
			
		||||
import com.fasterxml.jackson.databind.JsonNode;
 | 
			
		||||
import com.fasterxml.jackson.databind.ObjectMapper;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
import org.thingsboard.server.common.data.BaseData;
 | 
			
		||||
import org.thingsboard.server.common.data.id.DeviceCredentialsId;
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,8 @@ package org.thingsboard.server.transport.lwm2m.bootstrap.secure;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.annotation.JsonIgnore;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import org.eclipse.leshan.core.SecurityMode;
 | 
			
		||||
import org.eclipse.leshan.core.request.BindingMode;
 | 
			
		||||
import org.eclipse.leshan.core.util.Hex;
 | 
			
		||||
@ -41,7 +43,9 @@ public class LwM2MBootstrapConfig implements Serializable {
 | 
			
		||||
     *   notifIfDisabled: boolean,
 | 
			
		||||
     *   binding: string
 | 
			
		||||
     * */
 | 
			
		||||
    LwM2MBootstrapServers servers;
 | 
			
		||||
    @Getter
 | 
			
		||||
    @Setter
 | 
			
		||||
    private LwM2MBootstrapServers servers;
 | 
			
		||||
 | 
			
		||||
    /** -bootstrapServer, lwm2mServer
 | 
			
		||||
     * interface ServerSecurityConfig
 | 
			
		||||
@ -56,9 +60,13 @@ public class LwM2MBootstrapConfig implements Serializable {
 | 
			
		||||
     *   serverId?: number,
 | 
			
		||||
     *   bootstrapServerAccountTimeout: number
 | 
			
		||||
     * */
 | 
			
		||||
    LwM2MServerBootstrap bootstrapServer;
 | 
			
		||||
    @Getter
 | 
			
		||||
    @Setter
 | 
			
		||||
    private LwM2MServerBootstrap bootstrapServer;
 | 
			
		||||
 | 
			
		||||
    LwM2MServerBootstrap lwm2mServer;
 | 
			
		||||
    @Getter
 | 
			
		||||
    @Setter
 | 
			
		||||
    private LwM2MServerBootstrap lwm2mServer;
 | 
			
		||||
 | 
			
		||||
    @JsonIgnore
 | 
			
		||||
    public BootstrapConfig getLwM2MBootstrapConfig() {
 | 
			
		||||
 | 
			
		||||
@ -43,11 +43,11 @@ import java.util.Map;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap;
 | 
			
		||||
 | 
			
		||||
import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.BOOTSTRAP;
 | 
			
		||||
import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_ERROR;
 | 
			
		||||
import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_INFO;
 | 
			
		||||
import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY;
 | 
			
		||||
import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.getBootstrapParametersFromThingsboard;
 | 
			
		||||
import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.BOOTSTRAP;
 | 
			
		||||
 | 
			
		||||
@Slf4j
 | 
			
		||||
@Service("LwM2MBootstrapSecurityStore")
 | 
			
		||||
@ -86,10 +86,10 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
 | 
			
		||||
                    bootstrapConfigStore.add(endPoint, bsConfigNew);
 | 
			
		||||
                } catch (InvalidConfigurationException e) {
 | 
			
		||||
                    if (e.getMessage().contains("Psk identity") && e.getMessage().contains("already used for this bootstrap server")) {
 | 
			
		||||
                        log.trace("", e);
 | 
			
		||||
                        log.trace("Invalid Bootstrap Configuration", e);
 | 
			
		||||
                    }
 | 
			
		||||
                    else {
 | 
			
		||||
                        log.error("", e);
 | 
			
		||||
                        log.error("Invalid Bootstrap Configuration", e);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                return store.getSecurityInfo() == null ? null : Collections.singletonList(store.getSecurityInfo()).iterator();
 | 
			
		||||
@ -109,7 +109,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
 | 
			
		||||
                try {
 | 
			
		||||
                    bootstrapConfigStore.add(store.getEndpoint(), bsConfig);
 | 
			
		||||
                } catch (InvalidConfigurationException e) {
 | 
			
		||||
                    log.error("", e);
 | 
			
		||||
                    log.error("Invalid Bootstrap Configuration", e);
 | 
			
		||||
                }
 | 
			
		||||
                return store.getSecurityInfo();
 | 
			
		||||
            }
 | 
			
		||||
@ -158,11 +158,11 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
 | 
			
		||||
        LwM2MBootstrapConfig lwM2MBootstrapConfig = store.getBootstrapCredentialConfig();
 | 
			
		||||
        if (lwM2MBootstrapConfig != null) {
 | 
			
		||||
            BootstrapConfiguration bootstrapObject = getBootstrapParametersFromThingsboard(store.getDeviceProfile());
 | 
			
		||||
            lwM2MBootstrapConfig.servers = JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getServers()), LwM2MBootstrapServers.class);
 | 
			
		||||
            LwM2MServerBootstrap profileServerBootstrap = JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getBootstrapServer()), LwM2MServerBootstrap.class);
 | 
			
		||||
            if (SecurityMode.NO_SEC != profileServerBootstrap.getSecurityMode() && profileServerBootstrap != null) {
 | 
			
		||||
                profileServerBootstrap.setSecurityHost( profileServerBootstrap.getHost());
 | 
			
		||||
                profileServerBootstrap.setSecurityPort( profileServerBootstrap.getPort());
 | 
			
		||||
            lwM2MBootstrapConfig.setServers(JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getServers()), LwM2MBootstrapServers.class));
 | 
			
		||||
            LwM2MServerBootstrap bootstrapServerProfile = JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getBootstrapServer()), LwM2MServerBootstrap.class);
 | 
			
		||||
            if (SecurityMode.NO_SEC != bootstrapServerProfile.getSecurityMode() && bootstrapServerProfile != null) {
 | 
			
		||||
                bootstrapServerProfile.setSecurityHost( bootstrapServerProfile.getHost());
 | 
			
		||||
                bootstrapServerProfile.setSecurityPort( bootstrapServerProfile.getPort());
 | 
			
		||||
            }
 | 
			
		||||
            LwM2MServerBootstrap profileLwm2mServer = JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getLwm2mServer()), LwM2MServerBootstrap.class);
 | 
			
		||||
            if (SecurityMode.NO_SEC != profileLwm2mServer.getSecurityMode() && profileLwm2mServer != null) {
 | 
			
		||||
@ -173,9 +173,9 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
 | 
			
		||||
            TransportProtos.SessionInfoProto sessionInfo = helper.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits());
 | 
			
		||||
            bsSessions.put(store.getEndpoint(), sessionInfo);
 | 
			
		||||
            context.getTransportService().registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(null, null, null, sessionInfo, context.getTransportService()));
 | 
			
		||||
            if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) {
 | 
			
		||||
                lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap);
 | 
			
		||||
                lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer);
 | 
			
		||||
            if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.getBootstrapServer(), bootstrapServerProfile, lwM2MBootstrapConfig.getLwm2mServer(), profileLwm2mServer)) {
 | 
			
		||||
                lwM2MBootstrapConfig.setBootstrapServer(new LwM2MServerBootstrap(lwM2MBootstrapConfig.getBootstrapServer(), bootstrapServerProfile));
 | 
			
		||||
                lwM2MBootstrapConfig.setLwm2mServer(new LwM2MServerBootstrap(lwM2MBootstrapConfig.getLwm2mServer(), profileLwm2mServer));
 | 
			
		||||
                String logMsg = String.format("%s: getParametersBootstrap: %s Access connect client with bootstrap server.", LOG_LWM2M_INFO, store.getEndpoint());
 | 
			
		||||
                helper.sendParametersOnThingsboardTelemetry(helper.getKvStringtoThingsboard(LOG_LWM2M_TELEMETRY, logMsg), sessionInfo);
 | 
			
		||||
                return lwM2MBootstrapConfig;
 | 
			
		||||
@ -187,7 +187,6 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        log.error("Unable to decode Json or Certificate for [{}]", store.getEndpoint());
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
@ -197,13 +196,13 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
 | 
			
		||||
     * and (lwm2mServer  in credential and lwm2mServer  in profile
 | 
			
		||||
     *
 | 
			
		||||
     * @param bootstrapFromCredential - Bootstrap -> Security of bootstrapServer in credential
 | 
			
		||||
     * @param profileServerBootstrap  - Bootstrap -> Security of bootstrapServer in profile
 | 
			
		||||
     * @param bootstrapServerProfile  - Bootstrap -> Security of bootstrapServer in profile
 | 
			
		||||
     * @param lwm2mFromCredential     - Bootstrap -> Security of lwm2mServer in credential
 | 
			
		||||
     * @param profileLwm2mServer      - Bootstrap -> Security of lwm2mServer in profile
 | 
			
		||||
     * @return false if not sync between SecurityMode of Bootstrap credential and profile
 | 
			
		||||
     */
 | 
			
		||||
    private boolean getValidatedSecurityMode(LwM2MServerBootstrap bootstrapFromCredential, LwM2MServerBootstrap profileServerBootstrap, LwM2MServerBootstrap lwm2mFromCredential, LwM2MServerBootstrap profileLwm2mServer) {
 | 
			
		||||
        return (bootstrapFromCredential.getSecurityMode().equals(profileServerBootstrap.getSecurityMode()) &&
 | 
			
		||||
    private boolean getValidatedSecurityMode(LwM2MServerBootstrap bootstrapFromCredential, LwM2MServerBootstrap bootstrapServerProfile, LwM2MServerBootstrap lwm2mFromCredential, LwM2MServerBootstrap profileLwm2mServer) {
 | 
			
		||||
        return (bootstrapFromCredential.getSecurityMode().equals(bootstrapServerProfile.getSecurityMode()) &&
 | 
			
		||||
                lwm2mFromCredential.getSecurityMode().equals(profileLwm2mServer.getSecurityMode()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -200,21 +200,20 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession
 | 
			
		||||
    public BootstrapPolicy onRequestFailure(BootstrapSession bsSession,
 | 
			
		||||
                                            BootstrapDownlinkRequest<? extends LwM2mResponse> request, Throwable cause) {
 | 
			
		||||
        this.sendLogs (bsSession.getEndpoint(),
 | 
			
		||||
                String.format("%s: %s %s failed because of %s for %s : %s", LOG_LWM2M_INFO,
 | 
			
		||||
                        request.getClass().getSimpleName(), request.getPath().toString(), cause.toString(), bsSession.toString(), request.toString()));
 | 
			
		||||
                String.format("%s: %s %s failed because of %s for %s : %s", LOG_LWM2M_INFO, request.getClass().getSimpleName(),
 | 
			
		||||
                        request.getPath().toString(), cause.toString(), bsSession.toString(), request.toString()));
 | 
			
		||||
        return BootstrapPolicy.failed();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void end(BootstrapSession bsSession) {
 | 
			
		||||
        this.sendLogs (bsSession.getEndpoint(),
 | 
			
		||||
                String.format("%s: Bootstrap session finished : %s", LOG_LWM2M_INFO, bsSession.toString()));
 | 
			
		||||
        this.sendLogs (bsSession.getEndpoint(), String.format("%s: Bootstrap session finished : %s", LOG_LWM2M_INFO, bsSession.toString()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void failed(BootstrapSession bsSession, BootstrapFailureCause cause) {
 | 
			
		||||
        this.sendLogs (bsSession.getEndpoint(),
 | 
			
		||||
                String.format("%s: Bootstrap session failed by %s: %s", LOG_LWM2M_INFO, cause.toString(), bsSession.toString()));
 | 
			
		||||
        this.sendLogs (bsSession.getEndpoint(), String.format("%s: Bootstrap session failed by %s: %s", LOG_LWM2M_INFO,
 | 
			
		||||
                cause.toString(), bsSession.toString()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void sendLogs (String endpointName, String logMsg) {
 | 
			
		||||
 | 
			
		||||
@ -50,16 +50,13 @@ public enum LwM2mOperationType {
 | 
			
		||||
    WRITE_COMPOSITE(14, "WriteComposite", false, true),
 | 
			
		||||
    WRITE_ATTRIBUTES(15, "WriteAttributes", true),
 | 
			
		||||
    DELETE(16, "Delete", true),
 | 
			
		||||
 | 
			
		||||
    // only for RPC
 | 
			
		||||
    FW_UPDATE(17, "FirmwareUpdate", false),
 | 
			
		||||
    //        FW_READ_INFO(18, "FirmwareReadInfo", false),
 | 
			
		||||
    SW_UPDATE(19, "SoftwareUpdate", false),
 | 
			
		||||
    //        SW_READ_INFO(20, "SoftwareReadInfo", false),
 | 
			
		||||
    SW_UNINSTALL(21, "SoftwareUninstall", false),
 | 
			
		||||
    CREATE(11, "Create", true);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    CREATE(17, "Create", true);
 | 
			
		||||
    // only for RPC - future
 | 
			
		||||
//    FW_UPDATE(18, "FirmwareUpdate", false),
 | 
			
		||||
//    FW_READ_INFO(19, "FirmwareReadInfo", false),
 | 
			
		||||
//    SW_UPDATE(20, "SoftwareUpdate", false),
 | 
			
		||||
//    SW_READ_INFO(21, "SoftwareReadInfo", false),
 | 
			
		||||
//    SW_UNINSTALL(22, "SoftwareUninstall", false);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @Getter
 | 
			
		||||
 | 
			
		||||
@ -98,7 +98,9 @@ public class LwM2mServerListener {
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onError(Observation observation, Registration registration, Exception error) {
 | 
			
		||||
            log.error("Unable to handle notification of [{}:{}] [{}]", observation.getRegistrationId(), observation.getPath(), error.getMessage());
 | 
			
		||||
            if (error != null) {
 | 
			
		||||
                log.debug("Unable to handle notification of [{}:{}] [{}]", observation.getRegistrationId(), observation.getPath(), error.getMessage());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
 | 
			
		||||
@ -51,7 +51,7 @@ import static org.thingsboard.server.gen.transport.TransportProtos.KeyValueType.
 | 
			
		||||
public class LwM2mTransportServerHelper {
 | 
			
		||||
 | 
			
		||||
    private final LwM2mTransportContext context;
 | 
			
		||||
    private final static JsonParser JSON_PARSER = new JsonParser();;
 | 
			
		||||
    private final static JsonParser JSON_PARSER = new JsonParser();
 | 
			
		||||
 | 
			
		||||
    public void sendParametersOnThingsboardAttribute(List<TransportProtos.KeyValueProto> result, SessionInfoProto sessionInfo) {
 | 
			
		||||
        PostAttributeMsg.Builder request = PostAttributeMsg.newBuilder();
 | 
			
		||||
@ -231,7 +231,11 @@ public class LwM2mTransportServerHelper {
 | 
			
		||||
            case STRING_V:
 | 
			
		||||
                return kv.getStringV();
 | 
			
		||||
            case JSON_V:
 | 
			
		||||
                return JSON_PARSER.parse(kv.getJsonV());
 | 
			
		||||
                try {
 | 
			
		||||
                    return JSON_PARSER.parse(kv.getJsonV());
 | 
			
		||||
                } catch (Exception e) {
 | 
			
		||||
                    return null;
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -40,7 +40,6 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHa
 | 
			
		||||
import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest;
 | 
			
		||||
import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback;
 | 
			
		||||
import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
 | 
			
		||||
import org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService;
 | 
			
		||||
import org.thingsboard.server.transport.lwm2m.server.ota.LwM2MOtaUpdateService;
 | 
			
		||||
import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
 | 
			
		||||
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
 | 
			
		||||
@ -225,7 +224,7 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService {
 | 
			
		||||
                this.pushUpdateToClientIfNeeded(lwM2MClient, oldResourceValue, newValProto, pathIdVer, logFailedUpdateOfNonChangedValue, resourceModel.type);
 | 
			
		||||
            } else {
 | 
			
		||||
                pushUpdateMultiToClientIfNeeded(lwM2MClient, resourceModel, (JsonElement) newValProto,
 | 
			
		||||
                        (Map<Integer, LwM2mResourceInstance>) oldResourceValue, pathIdVer);
 | 
			
		||||
                        (Map<Integer, LwM2mResourceInstance>) oldResourceValue, pathIdVer, logFailedUpdateOfNonChangedValue);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
@ -251,7 +250,7 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void pushUpdateMultiToClientIfNeeded(LwM2mClient client, ResourceModel resourceModel, JsonElement newValProto,
 | 
			
		||||
                                                 Map<Integer, LwM2mResourceInstance> valueOld, String versionedId) {
 | 
			
		||||
                                                 Map<Integer, LwM2mResourceInstance> valueOld, String versionedId, boolean logFailedUpdateOfNonChangedValue) {
 | 
			
		||||
        Map newValues = convertMultiResourceValuesFromJson((JsonObject) newValProto, resourceModel.type, versionedId);
 | 
			
		||||
        if (newValues.size() > 0 && valueOld != null && valueOld.size() > 0) {
 | 
			
		||||
            valueOld.values().stream().forEach((v) -> {
 | 
			
		||||
@ -272,7 +271,7 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService {
 | 
			
		||||
        if (newValues.size() > 0) {
 | 
			
		||||
            TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId).value(newValues).timeout(this.config.getTimeout()).build();
 | 
			
		||||
            downlinkHandler.sendWriteReplaceRequest(client, request, new TbLwM2MWriteResponseCallback(uplinkHandler, logService, client, versionedId));
 | 
			
		||||
        } else {
 | 
			
		||||
        } else if (logFailedUpdateOfNonChangedValue) {
 | 
			
		||||
            log.warn("Didn't update resource [{}] [{}]", versionedId, newValProto);
 | 
			
		||||
            String logMsg = String.format("%s: Didn't update resource versionedId - %s value - %s. Value is not changed",
 | 
			
		||||
                    LOG_LWM2M_WARN, versionedId, newValProto);
 | 
			
		||||
 | 
			
		||||
@ -46,7 +46,6 @@ import java.io.Serializable;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.LinkedHashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
@ -106,7 +105,6 @@ public class LwM2mClient implements Serializable {
 | 
			
		||||
    @Getter
 | 
			
		||||
    private Long pagingTransmissionWindow;
 | 
			
		||||
    @Getter
 | 
			
		||||
    @Setter
 | 
			
		||||
    private Long edrxCycle;
 | 
			
		||||
    @Getter
 | 
			
		||||
    private Registration registration;
 | 
			
		||||
@ -122,9 +120,9 @@ public class LwM2mClient implements Serializable {
 | 
			
		||||
    private boolean firstEdrxDownlink = true;
 | 
			
		||||
 | 
			
		||||
    @Getter
 | 
			
		||||
    Set<ContentFormat> clientSupportContentFormats;
 | 
			
		||||
    private Set<ContentFormat> clientSupportContentFormats;
 | 
			
		||||
    @Getter
 | 
			
		||||
    ContentFormat defaultContentFormat;
 | 
			
		||||
    private ContentFormat defaultContentFormat;
 | 
			
		||||
    @Getter
 | 
			
		||||
    private final AtomicInteger retryAttempts;
 | 
			
		||||
 | 
			
		||||
@ -327,15 +325,18 @@ public class LwM2mClient implements Serializable {
 | 
			
		||||
        Collection<LwM2mResource> resources = ConcurrentHashMap.newKeySet();
 | 
			
		||||
        Map<Integer, ResourceModel> resourceModels = modelProvider.getObjectModel(registration)
 | 
			
		||||
                .getObjectModel(pathIds.getObjectId()).resources;
 | 
			
		||||
        if (params != null) {
 | 
			
		||||
        if (params != null && params instanceof Map && ((Map<?, ?>) params).size() > 0) {
 | 
			
		||||
            resourceModels.forEach((resourceId, resourceModel) -> {
 | 
			
		||||
                if (((Map) params).containsKey(String.valueOf(resourceId))) {
 | 
			
		||||
                    Object value = ((Map) params).get((String.valueOf(resourceId)));
 | 
			
		||||
                    LwM2mResource resource = null;
 | 
			
		||||
                    LwM2mResource resource;
 | 
			
		||||
                    if (resourceModel.multiple) {
 | 
			
		||||
                        if (value instanceof LinkedHashMap) {
 | 
			
		||||
                            Map values = convertMultiResourceValuesFromRpcBody((LinkedHashMap) value, resourceModel.type, pathRezIdVer);
 | 
			
		||||
                        try {
 | 
			
		||||
                            Map values = convertMultiResourceValuesFromRpcBody(value, resourceModel.type, pathRezIdVer);
 | 
			
		||||
                            resource = LwM2mMultipleResource.newResource(resourceId, (Map<Integer, ?>) values, resourceModel.type);
 | 
			
		||||
                        } catch (Exception e) {
 | 
			
		||||
                            throw new IllegalArgumentException("Resource id=" + resourceId + ", value = " + value + ", class = " +
 | 
			
		||||
                                    value.getClass().getSimpleName() + "is bad. Value of Multi-Instance Resource must be in Json format!");
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        Object valueRez = value.getClass().getSimpleName().equals("Integer") ? ((Integer) value).longValue() : value;
 | 
			
		||||
@ -353,9 +354,12 @@ public class LwM2mClient implements Serializable {
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
        else if (params == null) {
 | 
			
		||||
            throw new IllegalArgumentException("The value of this resource must not be null.");
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            throw new IllegalArgumentException("The value of this resource must be in Map format and size > 0");
 | 
			
		||||
        }
 | 
			
		||||
        return resources;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,7 @@ import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
 | 
			
		||||
import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
 | 
			
		||||
 | 
			
		||||
import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_WARN;
 | 
			
		||||
import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_ERROR;
 | 
			
		||||
 | 
			
		||||
@Slf4j
 | 
			
		||||
public abstract class AbstractTbLwM2MRequestCallback<R, T> implements DownlinkRequestCallback<R, T> {
 | 
			
		||||
@ -35,12 +35,12 @@ public abstract class AbstractTbLwM2MRequestCallback<R, T> implements DownlinkRe
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onValidationError(String params, String msg) {
 | 
			
		||||
        log.trace("[{}] Request [{}] validation failed. Reason: {}", client.getEndpoint(), params, msg);
 | 
			
		||||
        logService.log(client, String.format("[%s]: Request [%s] validation failed. Reason: %s", LOG_LWM2M_WARN, params, msg));
 | 
			
		||||
        logService.log(client, String.format("[%s]: Request [%s] validation failed. Reason: %s", LOG_LWM2M_ERROR, params, msg));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onError(String params, Exception e) {
 | 
			
		||||
        log.trace("[{}] Request [{}] processing failed", client.getEndpoint(), params, e);
 | 
			
		||||
        logService.log(client, String.format("[%s]: Request [%s] processing failed. Reason: %s", LOG_LWM2M_WARN, params, e));
 | 
			
		||||
        logService.log(client, String.format("[%s]: Request [%s] processing failed. Reason: %s", LOG_LWM2M_ERROR, params, e));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,6 @@ import lombok.RequiredArgsConstructor;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.eclipse.leshan.core.Link;
 | 
			
		||||
import org.eclipse.leshan.core.LwM2m;
 | 
			
		||||
import org.eclipse.leshan.core.ResponseCode;
 | 
			
		||||
import org.eclipse.leshan.core.attributes.Attribute;
 | 
			
		||||
import org.eclipse.leshan.core.attributes.AttributeSet;
 | 
			
		||||
import org.eclipse.leshan.core.model.LwM2mModel;
 | 
			
		||||
@ -79,7 +78,6 @@ import javax.annotation.PreDestroy;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
import java.util.LinkedHashMap;
 | 
			
		||||
import java.util.LinkedList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
@ -302,11 +300,13 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
 | 
			
		||||
                try {
 | 
			
		||||
                    WriteRequest downlink = null;
 | 
			
		||||
                    if (resourceModelWrite.multiple) {
 | 
			
		||||
                        if (request.getValue() instanceof Map && ((Map) request.getValue()).size() > 0) {
 | 
			
		||||
                        try {
 | 
			
		||||
                            Map value = convertMultiResourceValuesFromRpcBody(request.getValue(), resourceModelWrite.type, request.getObjectId());
 | 
			
		||||
                            downlink = new WriteRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(),
 | 
			
		||||
                                    (Map<Integer, ?>) request.getValue(), resourceModelWrite.type);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            callback.onValidationError(toString(request), "Resource value is: " + request.getValue().getClass().getSimpleName() + ". Value of Multi-Instance Resource must be in Json format!");
 | 
			
		||||
                                    value, resourceModelWrite.type);
 | 
			
		||||
                        } catch (Exception e) {
 | 
			
		||||
                            callback.onValidationError(toString(request), "Resource id=" + resultIds.toString() + ", value = " + request.getValue() +
 | 
			
		||||
                                    ", class = " + request.getValue().getClass().getSimpleName() + "is bad. Value of Multi-Instance Resource must be in Json format!");
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        downlink = this.getWriteRequestSingleResource(resourceModelWrite.type, contentFormat,
 | 
			
		||||
@ -368,12 +368,12 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
 | 
			
		||||
                        ResourceModel resourceModelWrite = client.getResourceModel(request.getVersionedId(), this.config.getModelProvider());
 | 
			
		||||
                        if (resourceModelWrite != null) {
 | 
			
		||||
                            if (resourceModelWrite.multiple) {
 | 
			
		||||
                                if (request.getValue() instanceof Map && ((Map) request.getValue()).size() > 0) {
 | 
			
		||||
                                    Map value = convertMultiResourceValuesFromRpcBody((LinkedHashMap) request.getValue(), resourceModelWrite.type, request.getObjectId());
 | 
			
		||||
                                try {
 | 
			
		||||
                                    Map value = convertMultiResourceValuesFromRpcBody(request.getValue(), resourceModelWrite.type, request.getObjectId());
 | 
			
		||||
                                    downlink = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(),
 | 
			
		||||
                                            value, resourceModelWrite.type);
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    callback.onValidationError(toString(request), "Resource value is bad. Format: " + request.getValue().getClass().getSimpleName() + ". Value of Multi-Instance Resource must be in Json format!");
 | 
			
		||||
                                } catch (Exception e1) {
 | 
			
		||||
                                    callback.onValidationError(toString(request), "Resource id=" + resultIds.toString() + ", value = " + request.getValue() + ", class = " + request.getValue().getClass().getSimpleName() + "is bad. Value of Multi-Instance Resource must be in Json format!");
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        } else {
 | 
			
		||||
 | 
			
		||||
@ -169,8 +169,6 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
 | 
			
		||||
                    case DISCOVER_ALL:
 | 
			
		||||
                        sendDiscoverAllRequest(client, rpcRequest);
 | 
			
		||||
                        break;
 | 
			
		||||
                    case FW_UPDATE:
 | 
			
		||||
                        //TODO: implement and add break statement
 | 
			
		||||
                    default:
 | 
			
		||||
                        throw new IllegalArgumentException("Unsupported operation: " + operationType.name());
 | 
			
		||||
                }
 | 
			
		||||
@ -257,11 +255,16 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
 | 
			
		||||
    private void sendWriteReplaceRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) {
 | 
			
		||||
        RpcWriteReplaceRequest requestBody = JacksonUtil.fromString(requestMsg.getParams(), RpcWriteReplaceRequest.class);
 | 
			
		||||
        LwM2mPath path = new LwM2mPath(fromVersionedIdToObjectId(versionedId));
 | 
			
		||||
        if (path.isResource() && requestBody.getValue() instanceof LinkedHashMap) {
 | 
			
		||||
        if (path.isResource()) {
 | 
			
		||||
            ResourceModel resourceModel = client.getResourceModel(versionedId, this.config.getModelProvider());
 | 
			
		||||
            if (resourceModel != null && resourceModel.multiple) {
 | 
			
		||||
                Map value = convertMultiResourceValuesFromRpcBody((LinkedHashMap) requestBody.getValue(), resourceModel.type, versionedId);
 | 
			
		||||
                requestBody.setValue(value);
 | 
			
		||||
                try {
 | 
			
		||||
                    Map value = convertMultiResourceValuesFromRpcBody(requestBody.getValue(), resourceModel.type, versionedId);
 | 
			
		||||
                    requestBody.setValue(value);
 | 
			
		||||
                } catch (Exception e) {
 | 
			
		||||
                    throw new IllegalArgumentException("Resource id=" + versionedId + ", value = " + requestBody.getValue() + ", class = " +
 | 
			
		||||
                            requestBody.getValue().getClass().getSimpleName() + "is bad. Value of Multi-Instance Resource must be in Json format!");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId)
 | 
			
		||||
 | 
			
		||||
@ -16,10 +16,7 @@
 | 
			
		||||
package org.thingsboard.server.transport.lwm2m.utils;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.core.type.TypeReference;
 | 
			
		||||
import com.fasterxml.jackson.databind.ObjectMapper;
 | 
			
		||||
import com.google.gson.Gson;
 | 
			
		||||
import com.google.gson.JsonObject;
 | 
			
		||||
import com.google.gson.JsonParser;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.apache.commons.lang3.StringUtils;
 | 
			
		||||
import org.eclipse.leshan.core.attributes.Attribute;
 | 
			
		||||
@ -47,6 +44,7 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportC
 | 
			
		||||
import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
 | 
			
		||||
import org.thingsboard.server.common.data.ota.OtaPackageKey;
 | 
			
		||||
import org.thingsboard.server.common.data.ota.OtaPackageType;
 | 
			
		||||
import org.thingsboard.server.common.transport.util.JsonUtils;
 | 
			
		||||
import org.thingsboard.server.transport.lwm2m.config.LwM2mVersion;
 | 
			
		||||
import org.thingsboard.server.transport.lwm2m.server.LwM2mOtaConvert;
 | 
			
		||||
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
 | 
			
		||||
@ -57,18 +55,15 @@ import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdate
 | 
			
		||||
import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdateResult;
 | 
			
		||||
import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdateState;
 | 
			
		||||
import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler;
 | 
			
		||||
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.LinkedHashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap;
 | 
			
		||||
 | 
			
		||||
import static org.eclipse.leshan.core.attributes.Attribute.DIMENSION;
 | 
			
		||||
import static org.eclipse.leshan.core.attributes.Attribute.GREATER_THAN;
 | 
			
		||||
@ -367,11 +362,9 @@ public class LwM2mTransportUtil {
 | 
			
		||||
        return String.format("%s %s", keyName, String.join(",", msgException)).trim();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Map convertMultiResourceValuesFromRpcBody(LinkedHashMap value, ResourceModel.Type type, String versionedId) {
 | 
			
		||||
        Gson gson = new Gson();
 | 
			
		||||
        JsonParser JSON_PARSER = new JsonParser();
 | 
			
		||||
        String json = gson.toJson(value, LinkedHashMap.class);
 | 
			
		||||
        return convertMultiResourceValuesFromJson((JsonObject) JSON_PARSER.parse(json), type, versionedId);
 | 
			
		||||
    public static Map convertMultiResourceValuesFromRpcBody(Object value, ResourceModel.Type type, String versionedId) throws Exception {
 | 
			
		||||
            String valueJsonStr = JsonUtils.riteValueAsString(value);
 | 
			
		||||
            return convertMultiResourceValuesFromJson((JsonObject) JsonUtils.parse(valueJsonStr), type, versionedId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Map convertMultiResourceValuesFromJson(JsonObject newValProto, ResourceModel.Type type, String versionedId) {
 | 
			
		||||
 | 
			
		||||
@ -15,15 +15,19 @@
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.common.transport.util;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.core.JsonProcessingException;
 | 
			
		||||
import com.fasterxml.jackson.databind.ObjectMapper;
 | 
			
		||||
import com.google.gson.JsonElement;
 | 
			
		||||
import com.google.gson.JsonObject;
 | 
			
		||||
import com.google.gson.JsonParser;
 | 
			
		||||
import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
public class JsonUtils {
 | 
			
		||||
 | 
			
		||||
    private static final JsonParser jsonParser = new JsonParser();
 | 
			
		||||
    private static final ObjectMapper json = new ObjectMapper();
 | 
			
		||||
 | 
			
		||||
    public static JsonObject getJsonObject(List<KeyValueProto> tsKv) {
 | 
			
		||||
        JsonObject json = new JsonObject();
 | 
			
		||||
@ -52,4 +56,9 @@ public class JsonUtils {
 | 
			
		||||
    public static JsonElement parse(String params) {
 | 
			
		||||
        return jsonParser.parse(params);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static String riteValueAsString (Object value) throws JsonProcessingException {
 | 
			
		||||
            return json.writeValueAsString(value);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -97,10 +97,10 @@ transport:
 | 
			
		||||
    timeout: "${CLIENT_SIDE_RPC_TIMEOUT:60000}"
 | 
			
		||||
  # Enable/disable http/mqtt/coap transport protocols (has higher priority than certain protocol's 'enabled' property)
 | 
			
		||||
  api_enabled: "${TB_TRANSPORT_API_ENABLED:true}"
 | 
			
		||||
  # Local LwM2M transport parameters
 | 
			
		||||
  log:
 | 
			
		||||
    enabled: "${TB_TRANSPORT_LOG_ENABLED:true}"
 | 
			
		||||
    max_length: "${TB_TRANSPORT_LOG_MAX_LENGTH:1024}"
 | 
			
		||||
  # Local LwM2M transport parameters
 | 
			
		||||
  lwm2m:
 | 
			
		||||
    # Enable/disable lvm2m transport protocol.
 | 
			
		||||
    enabled: "${LWM2M_ENABLED:true}"
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user