设备影子本质上是一份在服务器端缓存的设备数据(JSON 形式),为设备缓存的一份状态和配置数据。请参考官网 设备影子详情 设备影子数据流
作为中介,设备影子可以有效实现设备和用户应用之间的数据双向同步:
- 对于设备配置,用户应用不需要直接修改设备,只需要修改服务器端的设备影子,由设备影子同步到设备。即使当时设备不在线,设备上线后仍能从设备影子同步到最新配置。
- 对于设备状态,设备将状态上报到设备影子,用户应用查询时,只需查询设备影子即可。这样可以有效减少设备和服务器端的网络交互,尤其是低功耗设备。
编辑 app-config.json 文件中的配置信息
{
"PRODUCT_ID": "",
"DEVICE_NAME": "",
"DEVICE_PSK": "",
"SUB_PRODUCT_ID": "",
"SUB_DEV_NAME": "",
"SUB_PRODUCT_KEY": "",
"TEST_TOPIC": "",
"SHADOW_TEST_TOPIC": "",
"PRODUCT_KEY": ""
}
以密钥认证方式为例,需要在 app-config.json 填写 PRODUCT_ID(产品ID)、DEVICE_NAME(设备名称)、DEVICE_PSK(设备密钥)。
运行示例程序,切换底部Tab,选择设备影子模块,点击连接IOT
按钮,将设备进行认证连接到云端。示例代码如下:
mShadowSample.connect();
以下是设备成功上线并订阅设备影子 Topic 的logcat日志,在控制台中可查看创建的 gateway1 设备的状态已更新为上线。
I/TXMQTT_1.2.3: Start connecting to ssl://AP9ZLEVFKT.iotcloud.tencentdevices.com:8883
D/com.tencent.iot.hub.device.java.core.shadow.TXShadowConnection: onConnectCompleted, status[OK], reconnect[false], msg[connected to ssl://AP9ZLEVFKT.iotcloud.tencentdevices.com:8883]
D/com.tencent.iot.hub.device.java.core.shadow.TXShadowConnection: ******subscribe topic:$shadow/operation/result/AP9ZLEVFKT/gateway1
I/com.tencent.iot.hub.device.java.core.mqtt.TXMqttConnection: Starting subscribe topic: $shadow/operation/result/AP9ZLEVFKT/gateway1
D/com.tencent.iot.hub.device.java.core.shadow.TXShadowConnection: onSubscribeCompleted, status[OK], errMsg[subscribe success], topics[[$shadow/operation/result/AP9ZLEVFKT/gateway1]]
***subscribe topic:$shadow/operation/result/AP9ZLEVFKT/gateway1 success!!!!
D/ShadowSample: connect IoT completed, status[OK]
TXShadowActionCallBack为设备行为的回调说明
/**
* MQTT Connect完成回调
*
* @param status Status.OK: 连接成功; Status.ERROR: 连接失败
* @param reconnect true: 重新连接 false: 首次连接
* @param userContext 用户上下文
* @param msg 连接信息
*/
public void onConnectCompleted(Status status, boolean reconnect, Object userContext, String msg) {}
/**
* MQTT连接断开回调
*
* @param cause 连接断开原因
*/
public void onConnectionLost(Throwable cause) {}
/**
* 文档请求响应的回调接口
*
* @param type 文档操作方式, get/update/delete
* @param result 请求响应结果, 0: 成功;非0:失败
* @param jsonDocument 云端返回的json文档
*/
public void onRequestCallback(String type, int result, String jsonDocument) {}
/**
* 设备属性更新回调接口
*
* @param propertyJSONDocument 从云端收到的原始设备属性json文档
* @param propertyList 更新后的设备属性集
*/
public void onDevicePropertyCallback(String propertyJSONDocument, List<? extends DeviceProperty> propertyList) {}
/**
* 收到来自云端的消息
*
* @param topic 主题名称
* @param message 消息内容
*/
public void onMessageReceived(String topic, MqttMessage message) {}
/**
* 发布消息完成回调
*
* @param status Status.OK: 发布消息成功; Status.ERROR: 发布消息失败
* @param token 消息token,包含消息内容结构体
* @param userContext 用户上下文
* @param msg 详细信息
*/
public void onPublishCompleted(Status status, IMqttToken token, Object userContext, String msg) {}
/**
* 订阅主题完成回调
*
* @param status Status.OK: 订阅成功; Status.ERROR: 订阅失败
* @param token 消息token,包含消息内容结构体
* @param userContext 用户上下文
* @param msg 详细信息
*/
public void onSubscribeCompleted(Status status, IMqttToken token, Object userContext, String msg) {}
/**
* 取消订阅主题完成回调
*
* @param status Status.OK: 取消订阅成功; Status.ERROR: 取消订阅失败
* @param token 消息token,包含消息内容结构体
* @param userContext 用户上下文
* @param msg 详细信息
*/
public void onUnSubscribeCompleted(Status status, IMqttToken token, Object userContext, String msg) {}
可通过TXShadowConnection getConnectStatus()API获取设备连接状态
enum ConnectStatus {
kConnectIdle, //初始状态
kConnecting, // 连接中
kConnected, // 连接上/上线
kConnectFailed,// 连接失败
kDisconnected // 已断开连接
}
运行示例程序,在设备影子模块上,点击断开连接
按钮,断开 MQTT 认证连接。示例代码如下:
mShadowSample.closeConnect(); // 断开 MQTT 连接
以下是设备成功 取消订阅设备影子 Topic 并下线的logcat日志,在控制台中可查看创建的 gateway1 设备的状态已更新为离线。
I/com.tencent.iot.hub.device.java.core.mqtt.TXMqttConnection: Starting unSubscribe topic: $shadow/operation/result/AP9ZLEVFKT/gateway1
D/com.tencent.iot.hub.device.java.core.shadow.TXShadowConnection: onUnSubscribeCompleted, status[OK], errMsg[unsubscribe success], topics[[$shadow/operation/result/AP9ZLEVFKT/gateway1]]
D/com.tencent.iot.hub.device.java.core.shadow.TXShadowConnection: onDisconnectCompleted, status[OK], msg[disconnected to ssl://AP9ZLEVFKT.iotcloud.tencentdevices.com:8883]
运行示例程序,在设备影子模块上,点击注册设备属性
按钮,会创建 DeviceProperty 属性实例并将其添加到属性数组中,等待被上传更新。示例代码如下:
mShadowSample.registerProperty(); // 注册设备属性
以上方法会在 mShadowSample 维护一个 mDevicePropertyList 装着 DeviceProperty(设备属性)的 List ,当更新设备影子时会将 DeviceProperty(设备属性) 更新到云端的设备影子 json 中。
运行示例程序,在设备影子模块上,点击定时更新设备影子
按钮,在示例程序中会每隔10秒钟,更新设备属性信息。示例代码如下:
mShadowSample.loop(); // 每10秒钟更新一次设备属性信息
如果上一步点击了注册设备属性
按钮 ,则会把注册的属性信息更新到设备影子 json 中,观察Logcat日志。
D/com.tencent.iot.hub.device.java.core.shadow.TXShadowConnection: ******publish message id:62848
I/com.tencent.iot.hub.device.java.core.mqtt.TXMqttConnection: Starting publish topic: $shadow/operation/AP9ZLEVFKT/gateway1 Message: {"type":"update","state":{"reported":{"updateCount":1,"temperatureDesire":21}},"clientToken":"AP9ZLEVFKTgateway1-1","version":0}
I/com.tencent.iot.hub.device.java.core.mqtt.TXMqttConnection: deliveryComplete, token.getMessageId:0
D/com.tencent.iot.hub.device.java.core.shadow.TXShadowConnection: onPublishCompleted, status[OK], errMsg[publish success], topics[[$shadow/operation/AP9ZLEVFKT/gateway1]]
I/TXMQTT_1.2.3: Received topic: $shadow/operation/result/AP9ZLEVFKT/gateway1, id: 0, message: {"clientToken":"AP9ZLEVFKTgateway1-1","payload":{"state":{"reported":{"temperatureDesire":21,"updateCount":1}},"timestamp":1603252024994,"version":1},"result":0,"timestamp":1603252024994,"type":"update"}
D/IoTShadowFragment: onRequestCallback, type[update], result[0], document[{"state":{"reported":{"temperatureDesire":21,"updateCount":1}},"timestamp":1603252024994,"version":1}]
D/com.tencent.iot.hub.device.java.core.shadow.TXShadowConnection: ******update local mDocumentVersion to 1
...
从以上日志可以看出点击定时更新设备影子时,会将注册的属性信息先发布成一条带有 type 为 update 的 Topic 消息,发布消息成功回调后,由于 运行示例程序体验设备影子连接IoT云端 时,订阅过 $shadow/operation/result/${productId}/${deviceName}
Topic ,所以会收到带有设备属性的订阅的消息,同时更新本地 version ,用来判断消息中的 version 是否与设备影子服务端中的 version 一致。如果一致,则设备影子服务端执行更新设备影子流程。
运行示例程序,在设备影子模块上,点击获取设备文档
按钮,就会把设备影子最新的文档拉取下来。示例代码如下:
mShadowSample.getDeviceDocument(); // 拉取设备影子最新的文档
观察Logcat日志。
I/com.tencent.iot.hub.device.java.core.mqtt.TXMqttConnection: Starting publish topic: $shadow/operation/AP9ZLEVFKT/gateway1 Message: {"type":"get","clientToken":"AP9ZLEVFKTgateway1-23"}
D/com.tencent.iot.hub.device.java.core.shadow.TXShadowConnection: onPublishCompleted, status[OK], errMsg[publish success], topics[[$shadow/operation/AP9ZLEVFKT/gateway1]]
I/TXMQTT_1.2.3: Received topic: $shadow/operation/result/AP9ZLEVFKT/gateway1, id: 0, message: {"clientToken":"AP9ZLEVFKTgateway1-23","payload":{"state":{"reported":{"temperatureDesire":42,"updateCount":22}},"timestamp":1603252235473,"version":22},"result":0,"timestamp":1603252237,"type":"get"}
D/IoTShadowFragment: onRequestCallback, type[get], result[0], document[{"state":{"reported":{"temperatureDesire":42,"updateCount":22}},"timestamp":1603252235473,"version":22}]
D/com.tencent.iot.hub.device.java.core.shadow.TXShadowConnection: ******update local mDocumentVersion to 22
从以上日志可以看出,点击获取设备文档时,会发布一条带有 type 为 get 的 Topic 消息,由于 运行示例程序体验设备影子连接IoT云端 时,订阅过 $shadow/operation/result/${productId}/${deviceName}
Topic ,所以会收到设备影子最新的文档订阅的消息,在控制台中查看最新的设备影子文档可发现,和拉取得到的文档是一致的。
运行示例程序前,需要把将要订阅的 Topic 主题配置在 app-config.json 文件中的SHADOW_TEST_TOPIC(Topic权限),Topic的生成请参考 基于TCP的MQTT设备接入 控制台创建设备 中权限的使用。
运行示例程序,在设备影子模块上,点击订阅主题
按钮,订阅设备主题。示例代码如下:
mShadowSample.subscribeTopic(BuildConfig.SHADOW_TEST_TOPIC);
以下是设备成功订阅主题的logcat日志
I/com.tencent.iot.hub.device.java.core.mqtt.TXMqttConnection: Starting subscribe topic: AP9ZLEVFKT/gateway1/data
D/com.tencent.iot.hub.device.java.core.shadow.TXShadowConnection: onSubscribeCompleted, status[OK], errMsg[subscribe success], topics[[AP9ZLEVFKT/gateway1/data]]
设备之前订阅过的主题,可以取消订阅。
运行示例程序,在设备影子模块上,点击取消订阅主题
按钮,取消订阅。示例代码如下:
mShadowSample.unSubscribeTopic(BuildConfig.SHADOW_TEST_TOPIC);//取消订阅主题
以下是设备成功取消订阅的logcat日志
I/com.tencent.iot.hub.device.java.core.mqtt.TXMqttConnection: Starting unSubscribe topic: AP9ZLEVFKT/gateway1/data
D/com.tencent.iot.hub.device.java.core.shadow.TXShadowConnection: onUnSubscribeCompleted, status[OK], errMsg[unsubscribe success], topics[[AP9ZLEVFKT/gateway1/data]]
运行示例程序前,需要把将要发布的 Topic 主题配置在 app-config.json 文件中的SHADOW_TEST_TOPIC(Topic权限),Topic的生成请参考 基于TCP的MQTT设备接入 控制台创建设备 中权限的使用。
运行示例程序,在设备影子模块上,点击发布主题
按钮,发布设备主题。示例代码如下:
Map<String, String> data = new HashMap<String, String>(); // 要发布的数据
data.put("car_type", "suv"); // 车辆类型
data.put("oil_consumption", "6.6"); // 车辆油耗
data.put("maximum_speed", "205"); // 车辆最高速度
mShadowSample.publishTopic(BuildConfig.SHADOW_TEST_TOPIC, data); // 发布主题
以下是成功发布主题的logcat日志。
I/com.tencent.iot.hub.device.java.core.mqtt.TXMqttConnection: Starting publish topic: AP9ZLEVFKT/gateway1/data Message: {"oil_consumption":"6.6","maximum_speed":"205","car_type":"suv"}
D/IoTShadowFragment: onPublishCompleted, status[OK], topics[[AP9ZLEVFKT/gateway1/data]], userContext[MQTTRequest{requestType='publishTopic', requestId=3}], errMsg[publish success]