全部產品
Search
文件中心

IoT Platform:設定期望屬性值控制燈泡狀態

更新時間:Jun 30, 2024

物聯網平台提供設定期望屬性值功能,通過緩衝裝置屬性的期望值,實現從物聯網平台雲端控制裝置屬性值。本文介紹設定期望屬性值,實現從物聯網平台控制燈泡狀態的相關操作。

背景資訊

燈泡裝置接入物聯網平台後,若需從物聯網平台控制燈泡工作狀態(1:開啟;0:關閉),需要燈泡一直保持連網線上。實際情況下,燈泡可能無法一直線上。

您可在物聯網平台設定裝置期望屬性值,使其儲存在物聯網平台雲端。裝置線上後,可讀取物聯網平台儲存的期望屬性值,來更新自身屬性值。然後,裝置會將更新後的屬性值上報至物聯網平台,在物聯網平台的裝置運行狀態中顯示。

建立產品和裝置

  1. 登入物聯網平台控制台
  2. 執行個體概覽頁簽的全部環境下,找到對應的執行個體,單擊執行個體卡片。

  3. 在左側導覽列,選擇裝置管理 > 產品,單擊建立產品,建立一個產品:燈泡。
    建立產品
  4. 產品建立成功後,單擊前往定義物模型,為產品添加物模型並發布,請參見單個添加物模型
    如圖所示,本樣本添加屬性工作狀態(LightStatus)物模型
  5. 在左側導覽列,選擇裝置管理 > 裝置,單擊添加裝置,在燈泡產品下添加裝置:Lamp。
    添加裝置
    裝置添加成功後,擷取裝置認證資訊(ProductKey、DeviceName和DeviceSecret)。

    您可在裝置列表,單擊裝置Lamp對應的查看進入裝置詳情頁面,查看運行狀態,裝置屬性值和期望屬性值都為空白。此時期望屬性值版本為0。

    運行狀態初始

在雲端設定和查詢期望屬性值

您可在通過調用物聯網平台雲端API,設定和擷取裝置最新期望屬性值。

具體操作,請參見雲端API。本文以Java SDK(雲端)為例。

  • 調用SetDeviceDesiredProperty,設定期望屬性值。
    DefaultProfile profile = DefaultProfile.getProfile(
            "<RegionId>",          // 地區ID
            "<accessKey>",     //阿里雲帳號的AccessKey ID
            "<accessSecret>"); 阿里雲帳號AccessKey Secret
    IAcsClient client = new DefaultAcsClient(profile);
    
    // 建立API請求並設定參數
    SetDeviceDesiredPropertyRequest request = new SetDeviceDesiredPropertyRequest();
    request.setIotInstanceId("iot-060***");
    request.setDeviceName("Lamp");
    request.setProductKey("g4r***");
    // 待設定的屬性identifier與期望屬性值
    request.setItems("{\"LightStatus\": 1}");
    request.setVersions("{\"LightStatus\": 0}");
    
    // 發起請求並處理應答或異常
    try {
        SetDeviceDesiredPropertyResponse response = client.getAcsResponse(request);
        System.out.println(new Gson().toJson(response));
    } catch (ServerException e) {
        e.printStackTrace();
    } catch (ClientException e) {
        System.out.println("ErrCode:" + e.getErrCode());
        System.out.println("ErrMsg:" + e.getErrMsg());
        System.out.println("RequestId:" + e.getRequestId());
    }
  • 調用QueryDeviceDesiredProperty,查看裝置的期望屬性值。
    DefaultProfile profile = DefaultProfile.getProfile(
            "<RegionId>",          // 地區ID
            "<accessKey>",      /阿里雲帳號的AccessKey ID
            "<accessSecret>"); 阿里雲帳號Access Key Secret
    IAcsClient client = new DefaultAcsClient(profile);
    
    // 建立API請求並設定參數
    QueryDeviceDesiredPropertyRequest request = new QueryDeviceDesiredPropertyRequest();
    request.setIotInstanceId("iot-06****");
    request.setProductKey("g4r****");
    request.setDeviceName("Lamp");
    // 待查詢的屬性identifier列表。如不指定則查詢所有屬性(唯讀屬性除外)的期望屬性值。
    List<String> identifierList = new ArrayList<String>();
    identifierList.add("LightStatus");
    request.setIdentifiers(identifierList);
    
    // 發起請求並處理應答或異常
    try {
        QueryDeviceDesiredPropertyResponse response = client.getAcsResponse(request);
        System.out.println(new Gson().toJson(response));
    } catch (ServerException e) {
        e.printStackTrace();
    } catch (ClientException e) {
        System.out.println("ErrCode:" + e.getErrCode());
        System.out.println("ErrMsg:" + e.getErrMsg());
        System.out.println("RequestId:" + e.getRequestId());
    }

有關如何設定代碼中參數,請參見Java SDK使用說明

在物聯網平台雲端設定裝置期望屬性值後,裝置運行狀態顯示該值。

運行狀態期望

裝置端開發

裝置擷取期望屬性值,有兩種情境:

  • 燈泡重新上線時,主動擷取物聯網平台雲端緩衝的期望屬性值。
  • 燈泡正處於上線狀態,即時接收物聯網平台雲端推送的期望屬性值。

裝置端開發更多資訊,請參見使用裝置端SDK接入

本文提供了完整的裝置端Demo樣本,請參見下文附錄:裝置端Demo代碼

  1. 填入裝置認證、地區和MQTT接入地址的資訊。
    /**
    * 裝置認證資訊
    */
    private static String productKey = "******";
    private static String deviceName = "********";
    private static String deviceSecret = "**************";
    /**
    * MQTT串連資訊
    */
    private static String regionId = "******";
    
    ......
    
    /**
    * 設定 Mqtt 初始化參數
    */ 
    config.channelHost = deviceInfo.productKey + ".iot-as-mqtt." + region + ".aliyuncs.com:1883";
    說明
  2. 添加以下方法,用於變更實際燈泡的屬性,並在屬性變更後,主動將資訊上報到最新屬性值中。
    /**
     * 真實裝置處理屬性變更時,在以下兩個場下會被調用:
     * 情境1. 裝置連網後主動擷取最新的屬性期望值(由裝置發起,拉模式)
     * 情境2. 裝置線上時接收到雲端property.set推送的屬性期望值(由雲端發起,推模式)
     * @param identifier  屬性標識符
     * @param value       期望屬性值
     * @param needReport  是否通過property.post發送狀態上報。
     *                    上面情境2的處理函數中已整合屬性上報能力,會將needReport設定為false
     * @return
     */
    private boolean handlePropertySet(String identifier, ValueWrapper value, boolean needReport) {
        ALog.d(TAG, "真實裝置處理屬性變更 = [" + identifier + "], value = [" + value + "]");
        // 使用者根據實際情況判性是否設定成功,這裡測試直接返回成功
        boolean success = true;
        if (needReport) {
            reportProperty(identifier, value);
        }
        return success;
    }
    
    private void reportProperty(String identifier, ValueWrapper value){
        if (StringUtils.isEmptyString(identifier) || value == null) {
            return;
        }
    
        ALog.d(TAG, "上報屬性identity=" + identifier);
    
        Map<String, ValueWrapper> reportData = new HashMap<>();
        reportData.put(identifier, value);
        LinkKit.getInstance().getDeviceThing().thingPropertyPost(reportData, new IPublishResourceListener() {
    
            public void onSuccess(String s, Object o) {
                // 屬性上報成功
                ALog.d(TAG, "上報成功 onSuccess() called with: s = [" + s + "], o = [" + o + "]");
            }
    
            public void onError(String s, AError aError) {
                // 屬性上報失敗
                ALog.d(TAG, "上報失敗onError() called with: s = [" + s + "], aError = [" + JSON.toJSONString(aError) + "]");
            }
        });
    }
  3. 燈泡線上時,如果物聯網平台設定了燈泡的期望屬性值,該值將被推送到裝置端。燈泡處理訊息,改變屬性狀態。

    如下代碼中,將調用connectNotifyListener處理訊息,相關Alink協議說明,請參見裝置上報屬性

    收到非同步下行的資料後,mCommonHandler被調用,進而調用handlePropertySet更新裝置的物理屬性。

    /**
     * 註冊服務調用(以及屬性設定)的響應函數。
     * 雲端調用裝置的某項服務的時候,裝置端需要響應該服務並回複。
     */
    public void connectNotifyListener() {
        List<Service> serviceList = LinkKit.getInstance().getDeviceThing().getServices();
        for (int i = 0; serviceList != null && i < serviceList.size(); i++) {
            Service service = serviceList.get(i);
            LinkKit.getInstance().getDeviceThing().setServiceHandler(service.getIdentifier(), mCommonHandler);
        }
    }
    
    private ITResRequestHandler mCommonHandler = new ITResRequestHandler() {
        public void onProcess(String serviceIdentifier, Object result, ITResResponseCallback itResResponseCallback) {
            ALog.d(TAG, "onProcess() called with: s = [" + serviceIdentifier + "]," +
                    " o = [" + result + "], itResResponseCallback = [" + itResResponseCallback + "]");
            ALog.d(TAG, "收到雲端非同步服務調用 " + serviceIdentifier);
            try {
                if (SERVICE_SET.equals(serviceIdentifier)) {
                    Map<String, ValueWrapper> data = (Map<String, ValueWrapper>)((InputParams)result).getData();
                    ALog.d(TAG, "收到非同步下行資料 " + data);
                    // 設定真實裝置的屬性,然後上報設定完成的屬性值
                    boolean isSetPropertySuccess =
                            handlePropertySet("LightStatus", data.get("LightStatus"), false);
                    if (isSetPropertySuccess) {
                        if (result instanceof InputParams) {
                            // 響應雲端,接收資料成功
                            itResResponseCallback.onComplete(serviceIdentifier, null, null);
                        } else {
                            itResResponseCallback.onComplete(serviceIdentifier, null, null);
                        }
                    } else {
                        AError error = new AError();
                        error.setCode(100);
                        error.setMsg("setPropertyFailed.");
                        itResResponseCallback.onComplete(serviceIdentifier, new ErrorInfo(error), null);
                    }
                } else if (SERVICE_GET.equals(serviceIdentifier)) {
                } else {
                    // 根據不同的服務做不同的處理,跟具體的服務有關係
                    ALog.d(TAG, "根據真實的服務返回服務的值,請參照set樣本");
                    OutputParams outputParams = new OutputParams();
                    // outputParams.put("op", new ValueWrapper.IntValueWrapper(20));
                    itResResponseCallback.onComplete(serviceIdentifier, null, outputParams);
                }
            } catch (Exception e) {
                e.printStackTrace();
                ALog.d(TAG, "雲端返回資料格式異常");
            }
        }
    
        public void onSuccess(Object o, OutputParams outputParams) {
            ALog.d(TAG, "onSuccess() called with: o = [" + o + "], outputParams = [" + outputParams + "]");
            ALog.d(TAG, "註冊服務成功");
        }
    
        public void onFail(Object o, ErrorInfo errorInfo) {
            ALog.d(TAG, "onFail() called with: o = [" + o + "], errorInfo = [" + errorInfo + "]");
            ALog.d(TAG, "註冊服務失敗");
        }
    };
  4. 燈泡離線後,如果物聯網平台雲端設定了燈的期望屬性值,該值將被儲存在雲端。

    燈泡上線後,會主動擷取期望屬性值,然後調用handlePropertySet更新實際裝置的屬性。

    LinkKit.getInstance().init(params, new ILinkKitConnectListener() {
        public void onError(AError aError) {
            ALog.e(TAG, "Init Error error=" + aError);
        }
        public void onInitDone(InitResult initResult) {
            ALog.i(TAG, "onInitDone result=" + initResult);
    
            connectNotifyListener();
    
            // 擷取雲端最新期望屬性值
            getDesiredProperty(deviceInfo, Arrays.asList("LightStatus"), new IConnectSendListener() {
                public void onResponse(ARequest aRequest, AResponse aResponse) {
                    if(aRequest instanceof MqttPublishRequest && aResponse.data != null) {
                        JSONObject jsonObject = JSONObject.parseObject(aResponse.data.toString());
                        ALog.i(TAG, "onResponse result=" + jsonObject);
                        JSONObject dataObj = jsonObject.getJSONObject("data");
                        if (dataObj != null) {
                            if (dataObj.getJSONObject("LightStatus") == null) {
                                // 未設定期望值
                            } else {
                                Integer value = dataObj.getJSONObject("LightStatus").getInteger("value");
                                handlePropertySet("LightStatus", new ValueWrapper.IntValueWrapper(value), true);
                            }
                        }
                    }
                }
                public void onFailure(ARequest aRequest, AError aError) {
                    ALog.d(TAG, "onFailure() called with: aRequest = [" + aRequest + "], aError = [" + aError + "]");
                }
            });
        }
    });
    
    private void getDesiredProperty(BaseInfo info, List<String> properties, IConnectSendListener listener) {
        ALog.d(TAG, "getDesiredProperty() called with: info = [" + info + "], listener = [" + listener + "]");
        if(info != null && !StringUtils.isEmptyString(info.productKey) && !StringUtils.isEmptyString(info.deviceName)) {
            MqttPublishRequest request = new MqttPublishRequest();
            request.topic = DESIRED_PROPERTY_GET.replace("{productKey}", info.productKey).replace("{deviceName}", info.deviceName);
            request.replyTopic = DESIRED_PROPERTY_GET_REPLY.replace("{productKey}", info.productKey).replace("{deviceName}", info.deviceName);
            request.isRPC = true;
            RequestModel<List<String>> model = new RequestModel<>();
            model.id = String.valueOf(IDGeneraterUtils.getId());
            model.method = METHOD_GET_DESIRED_PROPERTY;
            model.params = properties;
            model.version = "1.0";
            request.payloadObj = model.toString();
            ALog.d(TAG, "getDesiredProperty: payloadObj=" + request.payloadObj);
            ConnectSDK.getInstance().send(request, listener);
        } else {
            ALog.w(TAG, "getDesiredProperty failed, baseInfo Empty.");
            if(listener != null) {
                AError error = new AError();
                error.setMsg("BaseInfoEmpty.");
                listener.onFailure(null, error);
            }
        }
    }

驗證結果

根據以下情境運行代碼,驗證燈泡線上、離線狀態,可在物聯網平台雲端通過設定期望屬性值,成功更改裝置屬性值。

  • 裝置線上時,雲端修改燈泡開關狀態,燈泡即時響應狀態變化。線上更新
  • 裝置離線後,如果雲端修改燈泡開關狀態,雲端期望屬性值與裝置的最新屬性值不一致。離線更新
  • 裝置重新連網上線後,裝置主動拉取期望屬性值,裝置的最新屬性值實現與雲端期望屬性值的同步。重連更新

附錄:裝置端Demo代碼

package com.aliyun.alink.devicesdk.demo;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.alink.apiclient.utils.StringUtils;
import com.aliyun.alink.dm.api.BaseInfo;
import com.aliyun.alink.dm.api.DeviceInfo;
import com.aliyun.alink.dm.api.InitResult;
import com.aliyun.alink.dm.model.RequestModel;
import com.aliyun.alink.dm.utils.IDGeneraterUtils;
import com.aliyun.alink.linkkit.api.ILinkKitConnectListener;
import com.aliyun.alink.linkkit.api.IoTMqttClientConfig;
import com.aliyun.alink.linkkit.api.LinkKit;
import com.aliyun.alink.linkkit.api.LinkKitInitParams;
import com.aliyun.alink.linksdk.cmp.api.ConnectSDK;
import com.aliyun.alink.linksdk.cmp.connect.channel.MqttPublishRequest;
import com.aliyun.alink.linksdk.cmp.core.base.ARequest;
import com.aliyun.alink.linksdk.cmp.core.base.AResponse;
import com.aliyun.alink.linksdk.cmp.core.listener.IConnectSendListener;
import com.aliyun.alink.linksdk.tmp.api.InputParams;
import com.aliyun.alink.linksdk.tmp.api.OutputParams;
import com.aliyun.alink.linksdk.tmp.device.payload.ValueWrapper;
import com.aliyun.alink.linksdk.tmp.devicemodel.Service;
import com.aliyun.alink.linksdk.tmp.listener.IPublishResourceListener;
import com.aliyun.alink.linksdk.tmp.listener.ITResRequestHandler;
import com.aliyun.alink.linksdk.tmp.listener.ITResResponseCallback;
import com.aliyun.alink.linksdk.tmp.utils.ErrorInfo;
import com.aliyun.alink.linksdk.tools.AError;
import com.aliyun.alink.linksdk.tools.ALog;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class LampDemo {
    private static final String TAG = "LampDemo";

    private final static String SERVICE_SET = "set";
    private final static String SERVICE_GET = "get";

    public static String DESIRED_PROPERTY_GET = "/sys/${productKey}/${deviceName}/thing/property/desired/get";
    public static String DESIRED_PROPERTY_GET_REPLY = "/sys/${productKey}/${deviceName}/thing/property/desired/get_reply";
    public static String METHOD_GET_DESIRED_PROPERTY = "thing.property.desired.get";

    public static void main(String[] args) {
        /**
         * 裝置認證資訊
         */
        String productKey = "****";
        String deviceName = "Lamp";
        String deviceSecret = "****";
        /**
         * mqtt串連資訊
         */
        String regionId = "cn-shanghai";

        LampDemo manager = new LampDemo();

        DeviceInfo deviceInfo = new DeviceInfo();
        deviceInfo.productKey = productKey;
        deviceInfo.deviceName = deviceName;
        deviceInfo.deviceSecret = deviceSecret;

        manager.init(deviceInfo, regionId);
    }

    public void init(final DeviceInfo deviceInfo, String region) {
        LinkKitInitParams params = new LinkKitInitParams();
        /**
         * 設定 Mqtt 初始化參數
         */
        IoTMqttClientConfig config = new IoTMqttClientConfig();
        config.productKey = deviceInfo.productKey;
        config.deviceName = deviceInfo.deviceName;
        config.deviceSecret = deviceInfo.deviceSecret;
        config.channelHost = deviceInfo.productKey + ".iot-as-mqtt." + region + ".aliyuncs.com:1883";
        /**
         * 是否接受離線訊息
         * 對應 mqtt 的 cleanSession 欄位
         */
        config.receiveOfflineMsg = false;
        params.mqttClientConfig = config;

        /**
         * 設定初始化,傳入裝置認證資訊
         */
        params.deviceInfo = deviceInfo;

        LinkKit.getInstance().init(params, new ILinkKitConnectListener() {
            public void onError(AError aError) {
                ALog.e(TAG, "Init Error error=" + aError);
            }
            public void onInitDone(InitResult initResult) {
                ALog.i(TAG, "onInitDone result=" + initResult);

                connectNotifyListener();

                // 擷取雲端最新期望屬性值
                getDesiredProperty(deviceInfo, Arrays.asList("LightStatus"), new IConnectSendListener() {
                    public void onResponse(ARequest aRequest, AResponse aResponse) {
                        if(aRequest instanceof MqttPublishRequest && aResponse.data != null) {
                            JSONObject jsonObject = JSONObject.parseObject(aResponse.data.toString());
                            ALog.i(TAG, "onResponse result=" + jsonObject);
                            JSONObject dataObj = jsonObject.getJSONObject("data");
                            if (dataObj != null) {
                                if (dataObj.getJSONObject("LightStatus") == null) {
                                    // 未設定期望值
                                } else {
                                    Integer value = dataObj.getJSONObject("LightStatus").getInteger("value");
                                    handlePropertySet("LightStatus", new ValueWrapper.IntValueWrapper(value), true);
                                }
                            }
                        }
                    }
                    public void onFailure(ARequest aRequest, AError aError) {
                        ALog.d(TAG, "onFailure() called with: aRequest = [" + aRequest + "], aError = [" + aError + "]");
                    }
                });
            }
        });
    }

    /**
     * 真實裝置處理屬性變更,兩個情境下會被調用:
     * 情境1. 裝置連網後主動擷取最新的屬性期望值(由裝置發起,拉模式)
     * 情境2. 裝置線上時接收到雲端property.set推送(由雲端發起,推模式)
     * @param identifier  屬性標識符
     * @param value       期望屬性值
     * @param needReport  是否發送property.post狀態上報。
     *                    上面情境2的處理函數中已整合屬性上報能力,會將needReport設定為false
     * @return
     */
    private boolean handlePropertySet(String identifier, ValueWrapper value, boolean needReport) {
        ALog.d(TAG, "真實裝置處理屬性變更 = [" + identifier + "], value = [" + value + "]");
        // 使用者根據實際情況判斷屬性是否設定成功,這裡測試直接返回成功
        boolean success = true;
        if (needReport) {
            reportProperty(identifier, value);
        }
        return success;
    }

    private void reportProperty(String identifier, ValueWrapper value){
        if (StringUtils.isEmptyString(identifier) || value == null) {
            return;
        }

        ALog.d(TAG, "上報屬性identity=" + identifier);

        Map<String, ValueWrapper> reportData = new HashMap<>();
        reportData.put(identifier, value);
        LinkKit.getInstance().getDeviceThing().thingPropertyPost(reportData, new IPublishResourceListener() {

            public void onSuccess(String s, Object o) {
                // 屬性上報成功
                ALog.d(TAG, "上報成功 onSuccess() called with: s = [" + s + "], o = [" + o + "]");
            }

            public void onError(String s, AError aError) {
                // 屬性上報失敗
                ALog.d(TAG, "上報失敗onError() called with: s = [" + s + "], aError = [" + JSON.toJSONString(aError) + "]");
            }
        });
    }

    /**
     * 註冊服務調用(以及屬性設定)的響應函數。
     * 雲端調用裝置的某項服務的時候,裝置端需要響應該服務並回複。
     */
    public void connectNotifyListener() {
        List<Service> serviceList = LinkKit.getInstance().getDeviceThing().getServices();
        for (int i = 0; serviceList != null && i < serviceList.size(); i++) {
            Service service = serviceList.get(i);
            LinkKit.getInstance().getDeviceThing().setServiceHandler(service.getIdentifier(), mCommonHandler);
        }
    }

    private ITResRequestHandler mCommonHandler = new ITResRequestHandler() {
        public void onProcess(String serviceIdentifier, Object result, ITResResponseCallback itResResponseCallback) {
            ALog.d(TAG, "onProcess() called with: s = [" + serviceIdentifier + "]," +
                    " o = [" + result + "], itResResponseCallback = [" + itResResponseCallback + "]");
            ALog.d(TAG, "收到雲端非同步服務調用 " + serviceIdentifier);
            try {
                if (SERVICE_SET.equals(serviceIdentifier)) {
                    Map<String, ValueWrapper> data = (Map<String, ValueWrapper>)((InputParams)result).getData();
                    ALog.d(TAG, "收到非同步下行資料 " + data);
                    // 設定真實裝置的屬性,然後上報設定完成的屬性值
                    boolean isSetPropertySuccess =
                            handlePropertySet("LightStatus", data.get("LightStatus"), false);
                    if (isSetPropertySuccess) {
                        if (result instanceof InputParams) {
                            // 響應雲端,接收資料成功
                            itResResponseCallback.onComplete(serviceIdentifier, null, null);
                        } else {
                            itResResponseCallback.onComplete(serviceIdentifier, null, null);
                        }
                    } else {
                        AError error = new AError();
                        error.setCode(100);
                        error.setMsg("setPropertyFailed.");
                        itResResponseCallback.onComplete(serviceIdentifier, new ErrorInfo(error), null);
                    }
                } else if (SERVICE_GET.equals(serviceIdentifier)) {
                } else {
                    // 根據不同的服務做不同的處理,跟具體的服務有關係
                    ALog.d(TAG, "使用者根據真實的服務返回服務的值,請參照set樣本");
                    OutputParams outputParams = new OutputParams();
                    // outputParams.put("op", new ValueWrapper.IntValueWrapper(20));
                    itResResponseCallback.onComplete(serviceIdentifier, null, outputParams);
                }
            } catch (Exception e) {
                e.printStackTrace();
                ALog.d(TAG, "雲端返回資料格式異常");
            }
        }

        public void onSuccess(Object o, OutputParams outputParams) {
            ALog.d(TAG, "onSuccess() called with: o = [" + o + "], outputParams = [" + outputParams + "]");
            ALog.d(TAG, "註冊服務成功");
        }

        public void onFail(Object o, ErrorInfo errorInfo) {
            ALog.d(TAG, "onFail() called with: o = [" + o + "], errorInfo = [" + errorInfo + "]");
            ALog.d(TAG, "註冊服務失敗");
        }
    };

    private void getDesiredProperty(BaseInfo info, List<String> properties, IConnectSendListener listener) {
        ALog.d(TAG, "getDesiredProperty() called with: info = [" + info + "], listener = [" + listener + "]");
        if(info != null && !StringUtils.isEmptyString(info.productKey) && !StringUtils.isEmptyString(info.deviceName)) {
            MqttPublishRequest request = new MqttPublishRequest();
            request.topic = DESIRED_PROPERTY_GET.replace("{productKey}", info.productKey).replace("{deviceName}", info.deviceName);
            request.replyTopic = DESIRED_PROPERTY_GET_REPLY.replace("{productKey}", info.productKey).replace("{deviceName}", info.deviceName);
            request.isRPC = true;
            RequestModel<List<String>> model = new RequestModel<>();
            model.id = String.valueOf(IDGeneraterUtils.getId());
            model.method = METHOD_GET_DESIRED_PROPERTY;
            model.params = properties;
            model.version = "1.0";
            request.payloadObj = model.toString();
            ALog.d(TAG, "getDesiredProperty: payloadObj=" + request.payloadObj);
            ConnectSDK.getInstance().send(request, listener);
        } else {
            ALog.w(TAG, "getDesiredProperty failed, baseInfo Empty.");
            if(listener != null) {
                AError error = new AError();
                error.setMsg("BaseInfoEmpty.");
                listener.onFailure(null, error);
            }
        }
    }
}