物聯網平臺提供設置期望屬性值功能,通過緩存設備屬性的期望值,實現從物聯網平臺云端控制設備屬性值。本文介紹設置期望屬性值,實現從物聯網平臺控制燈泡狀態的相關操作。
背景信息
燈泡設備接入物聯網平臺后,若需從物聯網平臺控制燈泡工作狀態(1:打開;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接入。
本文以Java SDK為例,提供了完整的設備端Demo示例,請參見下文附錄:設備端Demo代碼。
- 填入設備證書、地域和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";
說明- 設備證書信息,請參見本文上方創建產品和設備。
- regionId 為您的服務所在地域對應的Region ID。請在物聯網平臺控制臺左上角,查看您服務所在的地域。Region ID表達方法,請參見支持的地域。
- channelHost 為MQTT接入地址。獲取方法,請參見查看和配置實例終端節點信息(Endpoint)。
- 添加以下方法,用于變更實際燈泡的屬性,并在屬性變更后,主動將信息上報到最新屬性值中。
/** * 真實設備處理屬性變更時,在以下兩個場下會被調用: * 場景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) + "]"); } }); }
- 燈泡在線時,如果物聯網平臺設置了燈泡的期望屬性值,該值將被推送到設備端。燈泡處理消息,改變屬性狀態。
如下代碼中,將調用
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, "注冊服務失敗"); } };
- 燈泡離線后,如果物聯網平臺云端設置了燈的期望屬性值,該值將被存儲在云端。
燈泡上線后,會主動獲取期望屬性值,然后調用
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);
}
}
}
}
文檔內容是否對您有幫助?