IoT Platform allows you to specify a desired value for a device property. This way, you can cache the desired value in IoT Platform and remotely control a device by using the value. This topic describes how to specify a desired value for a device property in the IoT Platform console to control the status of a bulb.
Background information
After a bulb is connected to IoT Platform, make sure that the device remains online to control the bulb status. The bulb in the IoT Platform console can be in the On (1) or Off (0) state. The bulb may fail to remain online in actual scenarios.
You can specify a desired value for the bulb and store the value in IoT Platform. After the device goes online, the device can read the desired property value that is stored in IoT Platform and update the property value. Then, the updated property value is submitted to IoT Platform and displayed on the Status tab in the IoT Platform console.
Create a product and add a device
- Log on to the IoT Platform console.
On the Overview page, find the instance that you want to manage and click the instance ID or instance name.
- In the left-side navigation pane, choose . On the Products page, click Create Product to create a bulb product.

- After the product is created, click Define TSL Model to add a Thing Specification Language (TSL) model to the product and then publish it. For more information, see Add a TSL model for a single product.
In this example, add a property named Work Status (LightStatus). The data type for this property is
bool (Boolean), with0representing Off and1representing On. - In the left-side navigation pane, choose , and click Add Device to add the Lamp device to the product.
After the device is created, obtain its device certificate, which includes the ProductKey, DeviceName, and DeviceSecret.
In the device list, find the
Lampdevice and click View to go to the Device Details page. On the Running Status tab, you will see that the device's property value and desired property value are both empty. Initially, the desired property version is 0.
Set and query desired property values
You can call API operations to set and query the latest desired property values for a device.
The examples in this topic use the Java SDK (for cloud-side development). For more information, see API operations.
- Call the SetDeviceDesiredProperty operation to set a desired property value.
DefaultProfile profile = DefaultProfile.getProfile( "<RegionId>", // The region ID. "<accessKey>", // The AccessKey ID of your Alibaba Cloud account. "<accessSecret>"); // The AccessKey secret of your Alibaba Cloud account. IAcsClient client = new DefaultAcsClient(profile); // Create an API request and set the parameters. SetDeviceDesiredPropertyRequest request = new SetDeviceDesiredPropertyRequest(); request.setIotInstanceId("iot-060***"); request.setDeviceName("Lamp"); request.setProductKey("g4r***"); // The property identifier and the desired value to set. request.setItems("{\"LightStatus\": 1}"); request.setVersions("{\"LightStatus\": 0}"); // Send the request and handle the response or exceptions. 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()); } - Call the QueryDeviceDesiredProperty operation to view the desired property values of the device.
DefaultProfile profile = DefaultProfile.getProfile( "<RegionId>", // The region ID. "<accessKey>", // The AccessKey ID of your Alibaba Cloud account. "<accessSecret>"); // The AccessKey secret of your Alibaba Cloud account. IAcsClient client = new DefaultAcsClient(profile); // Create an API request and set the parameters. QueryDeviceDesiredPropertyRequest request = new QueryDeviceDesiredPropertyRequest(); request.setIotInstanceId("iot-06****"); request.setProductKey("g4r****"); request.setDeviceName("Lamp"); // A list of property identifiers to query. If this parameter is omitted, the desired values of all writable properties are queried. List<String> identifierList = new ArrayList<String>(); identifierList.add("LightStatus"); request.setIdentifiers(identifierList); // Send the request and handle the response or exceptions. 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()); }
For more information about how to set parameters in the code, see Java SDK usage guide.
After you set the desired property value in IoT Platform, the value appears in the device's running status. On the Device Details page, click TSL Data > Running Status. You can see that the desired value of the property is displayed as 1 (On).
Device-side development
The bulb can obtain the desired property values in the following scenarios:
- If the bulb goes from offline to online, the device requests the desired property value that is cached in IoT Platform.
- If the bulb is online, the device receives the desired property value that is pushed by IoT Platform in real time.
For more information about device-side development, see Use a device-side SDK to connect a device.
This topic provides a complete device-side demo . For the full code, see Appendix: Device-side demo code.
- Fill in the device certificate, region, and MQTT endpoint information.
/** * Device certificate information. */ private static String productKey = "******"; private static String deviceName = "********"; private static String deviceSecret = "**************"; /** * MQTT connection information. */ private static String regionId = "******"; ...... /** * Set MQTT initialization parameters. */ config.channelHost = deviceInfo.productKey + ".iot-as-mqtt." + region + ".aliyuncs.com:1883";Note- For information about the device certificate, see the Create a product and a device section in this topic.
- The regionId is the ID of the region where your instance is deployed. You can find the region in the upper-left corner of the IoT Platform console. For a list of region IDs, see Supported regions.
- The channelHost parameter specifies the MQTT endpoint. For more information about how to obtain an endpoint, see View and configure instance endpoints.
- Add the following methods to change the light bulb's physical state and report the new state to the cloud.
/** * This method is called when the physical device needs to handle a property change. This occurs in two scenarios: * Scenario 1: After connecting, the device actively fetches the latest desired property values (pull model). * Scenario 2: While online, the device receives desired property values pushed from the cloud through `property.set` (push model). * @param identifier The property identifier. * @param value The desired property value. * @param needReport Specifies whether to send a status report by using `property.post`. * In Scenario 2, the handler already includes reporting, so this parameter is set to false. * @return */ private boolean handlePropertySet(String identifier, ValueWrapper value, boolean needReport) { ALog.d(TAG, "Physical device handling property change = [" + identifier + "], value = [" + value + "]"); // In a real application, you would determine whether the set operation was successful. Here, we assume success. 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, "Reporting property 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) { // The property is reported successfully. ALog.d(TAG, "Report successful. onSuccess() called with: s = [" + s + "], o = [" + o + "]"); } public void onError(String s, AError aError) { // Failed to report the property. ALog.d(TAG, "Report failed. onError() called with: s = [" + s + "], aError = [" + JSON.toJSONString(aError) + "]"); } }); } - When the light bulb is online, if a desired property value is set in IoT Platform, the value is pushed to the device. The device processes the message and changes its state.
In the following code, the
connectNotifyListeneris called to process the message. For more information about the Alink protocol, see Device reports properties.After the asynchronous downstream data is received,
mCommonHandleris called, which in turn callshandlePropertySetto update the physical properties of the device./** * Register a response function for service calls and property settings. * When the cloud calls a device service, the device must respond to the service call. */ 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, "Received asynchronous service call from cloud: " + serviceIdentifier); try { if (SERVICE_SET.equals(serviceIdentifier)) { Map<String, ValueWrapper> data = (Map<String, ValueWrapper>)((InputParams)result).getData(); ALog.d(TAG, "Received asynchronous downstream data: " + data); // Set the property on the physical device and then report the updated value. boolean isSetPropertySuccess = handlePropertySet("LightStatus", data.get("LightStatus"), false); if (isSetPropertySuccess) { if (result instanceof InputParams) { // Respond to the cloud that the data was received successfully. 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 { // Handle other services based on specific requirements. ALog.d(TAG, "Return a value based on the actual service. For an example, refer to the 'set' implementation."); OutputParams outputParams = new OutputParams(); // outputParams.put("op", new ValueWrapper.IntValueWrapper(20)); itResResponseCallback.onComplete(serviceIdentifier, null, outputParams); } } catch (Exception e) { e.printStackTrace(); ALog.d(TAG, "Invalid data format returned from the cloud."); } } public void onSuccess(Object o, OutputParams outputParams) { ALog.d(TAG, "onSuccess() called with: o = [" + o + "], outputParams = [" + outputParams + "]"); ALog.d(TAG, "Service registered successfully."); } public void onFail(Object o, ErrorInfo errorInfo) { ALog.d(TAG, "onFail() called with: o = [" + o + "], errorInfo = [" + errorInfo + "]"); ALog.d(TAG, "Failed to register service."); } }; - If you set a desired property value in IoT Platform while the light bulb is offline, IoT Platform stores the value in the cloud.
When the light bulb comes online, it actively fetches the desired value and then calls
handlePropertySetto update its physical state.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(); // Get the latest desired property value from the cloud. 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) { // No desired value has been set. } 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); } } }
Verify the migration result
Run the sample code based on the following scenarios to verify the online or offline state of the bulb. You can change the device property value by specifying a desired property value in IoT Platform.
- When the device is online, changing the bulb's status in the cloud triggers a real-time response. On the Device Details page, navigate to TSL Data > Running Status. The property value for Work Status is 1 (On), and the desired value is also 1 (On).
- If the device is offline and you change the bulb's status in the cloud, the desired value will differ from the device's last reported value. On the TSL Data > Running Status tab of the Device Details page, the current value of the Work Status property is 1 (On), while the desired value is 0 (Off).
- After the device reconnects, it fetches the desired property value and synchronizes its state. On the TSL Data > Running Status tab, you can see that both the desired and current values for the Work Status property are
0 (Off). This confirms that the device's property has synchronized with the desired value from the cloud.
Appendix: Sample code to configure a device
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) {
/**
* Device certificate information.
*/
String productKey = "****";
String deviceName = "Lamp";
String deviceSecret = "****";
/**
* MQTT connection information.
*/
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();
/**
* Set MQTT initialization parameters.
*/
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";
/**
* Specifies whether to receive offline messages.
* This corresponds to the `cleanSession` field in MQTT.
*/
config.receiveOfflineMsg = false;
params.mqttClientConfig = config;
/**
* Set initialization parameters and pass in the device certificate information.
*/
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();
// Get the latest desired property value from the cloud.
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) {
// No desired value has been set.
} 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 + "]");
}
});
}
});
}
/**
* This method is called when the physical device needs to handle a property change. This occurs in two scenarios:
* Scenario 1: After connecting, the device actively fetches the latest desired property values (pull model).
* Scenario 2: While online, the device receives desired property values pushed from the cloud through `property.set` (push model).
* @param identifier The property identifier.
* @param value The desired property value.
* @param needReport Specifies whether to send a status report by using `property.post`.
* In Scenario 2, the handler already includes reporting, so this parameter is set to false.
* @return
*/
private boolean handlePropertySet(String identifier, ValueWrapper value, boolean needReport) {
ALog.d(TAG, "Physical device handling property change = [" + identifier + "], value = [" + value + "]");
// In a real application, you would determine whether the set operation was successful. Here, we assume success.
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, "Reporting property 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) {
// The property is reported successfully.
ALog.d(TAG, "Report successful. onSuccess() called with: s = [" + s + "], o = [" + o + "]");
}
public void onError(String s, AError aError) {
// Failed to report the property.
ALog.d(TAG, "Report failed. onError() called with: s = [" + s + "], aError = [" + JSON.toJSONString(aError) + "]");
}
});
}
/**
* Register a response function for service calls and property settings.
* When the cloud calls a device service, the device must respond to the service call.
*/
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, "Received asynchronous service call from cloud: " + serviceIdentifier);
try {
if (SERVICE_SET.equals(serviceIdentifier)) {
Map<String, ValueWrapper> data = (Map<String, ValueWrapper>)((InputParams)result).getData();
ALog.d(TAG, "Received asynchronous downstream data: " + data);
// Set the property on the physical device and then report the updated value.
boolean isSetPropertySuccess =
handlePropertySet("LightStatus", data.get("LightStatus"), false);
if (isSetPropertySuccess) {
if (result instanceof InputParams) {
// Respond to the cloud that the data was received successfully.
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 {
// Handle other services based on their specific requirements.
ALog.d(TAG, "Return a value based on the actual service. For an example, refer to the 'set' implementation.");
OutputParams outputParams = new OutputParams();
// outputParams.put("op", new ValueWrapper.IntValueWrapper(20));
itResResponseCallback.onComplete(serviceIdentifier, null, outputParams);
}
} catch (Exception e) {
e.printStackTrace();
ALog.d(TAG, "Invalid data format returned from the cloud.");
}
}
public void onSuccess(Object o, OutputParams outputParams) {
ALog.d(TAG, "onSuccess() called with: o = [" + o + "], outputParams = [" + outputParams + "]");
ALog.d(TAG, "Service registered successfully.");
}
public void onFail(Object o, ErrorInfo errorInfo) {
ALog.d(TAG, "onFail() called with: o = [" + o + "], errorInfo = [" + errorInfo + "]");
ALog.d(TAG, "Failed to register service.");
}
};
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);
}
}
}
}