一、前提条件
在使用这篇能力前,请确保已完成以下准备工作:
·已开通腾讯云物联网相关服务,并在控制台完成实例、应用和设备的准备工作。可参见 开通服务。
·已在客户端工程中集成 IoT 应用端 SDK 并完成登录。可参见 Android SDK 集成与登录、iOS SDK 集成与登录、小程序 SDK 集成与登录、鸿蒙 SDK 集成与登录。
·已获取目标家庭的 familyId。首次使用的账号需要先查询或创建家庭,参见 家庭管理。
·绑定设备前,业务后台已具备签发设备绑定签名(deviceBindSignature)的能力。
二、获取设备管理对象
登录成功后,通过 TXIoTEngine 单例获取 TXIoTDeviceManager。该对象是这篇所有设备操作的入口,可长期持有。
AndroidiOS小程序鸿蒙TXIoTEngine iotEngine = TXIoTEngine.getInstance(context);TXIoTDeviceManager deviceManager = iotEngine.getDeviceManager();if (deviceManager == null) { // SDK 未登录或登录已过期,请先完成登录。 return;}TXIoTDeviceManager *deviceManager = [[TXIoTEngine getInstance] getDeviceManager];if (deviceManager == nil) { // SDK 未登录或登录已过期,请先完成登录。 return;}const { TXIoTEngine } = requirePlugin('tx-iot-sdk');// getInstance 为异步方法,首次调用会等待内部模块加载完成。const iotEngine = await TXIoTEngine.getInstance();const deviceManager = iotEngine.getDeviceManager();if (deviceManager == null) { // SDK 未登录或登录已过期,请先完成登录。 return;}import { getIoTEngineInstance, TXIoTEngine } from '@tencentcloud/liteavsdk_iot'const iotEngine: TXIoTEngine = getIoTEngineInstance()const deviceManager = iotEngine.getDeviceManager()if (deviceManager == null) { // SDK 未登录或登录已过期,请先完成登录。 return}
注意:说明:
注意:getDeviceManager 返回空值(Android、小程序与鸿蒙为 null,iOS 为 nil)表示 SDK 尚未登录或登录已过期。请在登录成功回调之后再获取该对象,不要在 App 启动阶段提前缓存。
三、绑定流程
一次完整的设备绑定涉及三方协作:
1. App 侧通过配网、扫码等方式获得设备的产品 ID 与设备名称。
2. App 请求业务后台签发该设备的绑定签名 deviceBindSignature。签名由业务后台使用密钥生成,客户端不参与计算。
3. App 调用 bindDevice,传入目标 familyId 与该签名完成绑定。
绑定成功后回调返回 TXIoTDeviceInfo。
四、调试阶段获取设备绑定签名
正式环境中 deviceBindSignature 由业务后台签发。若尚在联调阶段、业务后台尚未就绪,可通过云 API 在线调试临时获取一个签名用于验证客户端流程:
1. 打开 GenSingleDeviceSignatureOfPublic 接口。
2. 填入控制台上的 ProductId 与 DeviceName,Expire 按调试需要设置。
3. 单击发送请求,从返回结果的 Response.DeviceSignature 中取出签名值。
五、绑定与解绑接口
| 方法 | 说明 |
| bindDevice | 将设备绑定到指定家庭,需传入 familyId 与 deviceBindSignature,成功返回 TXIoTDeviceInfo。 |
| unbindDevice | 将设备从指定家庭移除,deviceId 为 TXIoTDeviceId。解绑后该家庭下的所有成员都将失去该设备的访问权限,依附于该设备的分享关系也随之失效,参见 设备分享。 |
AndroidiOS小程序鸿蒙// 绑定设备:deviceBindSignature 由业务后台签发。deviceManager.bindDevice(familyId, deviceBindSignature, new TXIoTCallback<TXIoTDeviceInfo>() { @Override public void onSuccess(TXIoTDeviceInfo deviceInfo) { TXIoTDeviceId deviceId = deviceInfo.deviceId; boolean isOnline = deviceInfo.status != null && deviceInfo.status.isOnline; } @Override public void onError(TXIoTErrorCode errorCode, String errorMessage) { // 依次排查:签名是否过期、签名与设备是否匹配、设备是否已被绑定。 }});// 解绑设备。deviceManager.unbindDevice(familyId, deviceId, new TXIoTCallback<Void>() { @Override public void onSuccess(Void result) { // 解绑成功,请同步刷新本地设备列表。 } @Override public void onError(TXIoTErrorCode errorCode, String errorMessage) { // 解绑失败,请根据错误码和错误信息处理。 }});// 绑定设备:deviceBindSignature 由业务后台签发。TXIoTCallback<TXIoTDeviceInfo *> *bindCallback = [[TXIoTCallback alloc] init];bindCallback.onSuccess = ^(TXIoTDeviceInfo *deviceInfo) { TXIoTDeviceId *deviceId = deviceInfo.deviceId; BOOL isOnline = deviceInfo.status.isOnline;};bindCallback.onError = ^(TXIoTErrorCode errorCode, NSString *errorMessage) { // 依次排查:签名是否过期、签名与设备是否匹配、设备是否已被绑定。};[deviceManager bindDevice:familyId deviceBindSignature:deviceBindSignature callback:bindCallback];// 解绑设备。TXIoTVoidCallback *unbindCallback = [[TXIoTVoidCallback alloc] init];unbindCallback.onSuccess = ^{ // 解绑成功,请同步刷新本地设备列表。};unbindCallback.onError = ^(TXIoTErrorCode errorCode, NSString *errorMessage) { // 解绑失败,请根据错误码和错误信息处理。};[deviceManager unbindDevice:familyId deviceId:deviceId callback:unbindCallback];// 绑定设备:deviceBindSignature 由业务后台签发。try { const deviceInfo = await deviceManager.bindDevice(familyId, deviceBindSignature); const deviceId = deviceInfo.deviceId; const isOnline = deviceInfo.status != null && deviceInfo.status.isOnline;} catch (err) { // err 为 { code, message },code 取值见 TXIoTErrorCode。 // 依次排查:签名是否过期、签名与设备是否匹配、设备是否已被绑定。}// 解绑设备。try { await deviceManager.unbindDevice(familyId, deviceId); // 解绑成功,请同步刷新本地设备列表。} catch (err) { // 解绑失败,请根据 err.code 和 err.message 处理。}// 绑定设备:deviceBindSignature 由业务后台签发。deviceManager.bindDevice(familyId, deviceBindSignature, { onSuccess: (deviceInfo) => { const deviceId = deviceInfo.deviceId const isOnline = deviceInfo.status != null && deviceInfo.status.isOnline }, onError: (errorCode, errorMessage) => { // 依次排查:签名是否过期、签名与设备是否匹配、设备是否已被绑定。 }})// 解绑设备。deviceManager.unbindDevice(familyId, deviceId, { onSuccess: () => { // 解绑成功,请同步刷新本地设备列表。 }, onError: (errorCode, errorMessage) => { // 解绑失败,请根据错误码和错误信息处理。 }})
六、查询设备列表
getDeviceList 按家庭维度分页返回设备。分页规则如下:
·首次查询传入空字符串 ""。
·回调返回的 nextPageToken 为空字符串时表示已是最后一页;非空时原样回传给下一次 getDeviceList 即可,不要自行解析或拼接该值。
·返回的每个 TXIoTDeviceInfo 已包含 status.isOnline 在线状态。
| 方法 | 说明 |
| getDeviceList | 分页查询指定家庭下的设备列表。 |
AndroidiOS小程序鸿蒙// 首次查询传空字符串;nextPageToken 原样回传以获取下一页。deviceManager.getDeviceList(familyId, "", new TXIoTCallback<TXIoTPageResult<TXIoTDeviceInfo>>() { @Override public void onSuccess(TXIoTPageResult<TXIoTDeviceInfo> result) { List<TXIoTDeviceInfo> deviceList = result.dataList; String nextPageToken = result.nextPageToken; if (nextPageToken != null && !nextPageToken.isEmpty()) { // 仍有下一页,可继续调用 getDeviceList(familyId, nextPageToken, …)。 } } @Override public void onError(TXIoTErrorCode errorCode, String errorMessage) { // 查询设备列表失败,请根据错误码和错误信息处理。 }});// 首次查询传空字符串;nextPageToken 原样回传以获取下一页。TXIoTCallback<TXIoTPageResult<TXIoTDeviceInfo *> *> *listCallback = [[TXIoTCallback alloc] init];listCallback.onSuccess = ^(TXIoTPageResult<TXIoTDeviceInfo *> *result) { NSArray<TXIoTDeviceInfo *> *deviceList = result.dataList; NSString *nextPageToken = result.nextPageToken; if (nextPageToken.length > 0) { // 仍有下一页,可继续调用 getDeviceList:nextPageToken:callback:。 }};listCallback.onError = ^(TXIoTErrorCode errorCode, NSString *errorMessage) { // 查询设备列表失败,请根据错误码和错误信息处理。};[deviceManager getDeviceList:familyId nextPageToken:@"" callback:listCallback];// 首次查询传空字符串;nextPageToken 原样回传以获取下一页。try { const result = await deviceManager.getDeviceList(familyId, ''); const deviceList = result.dataList; const nextPageToken = result.nextPageToken; if (nextPageToken) { // 仍有下一页,可继续调用 getDeviceList(familyId, nextPageToken)。 }} catch (err) { // 查询设备列表失败,请根据 err.code 和 err.message 处理。}// 首次查询传空字符串;nextPageToken 原样回传以获取下一页。deviceManager.getDeviceList(familyId, '', { onSuccess: (result) => { const deviceList = result.dataList const nextPageToken = result.nextPageToken if (nextPageToken !== '') { // 仍有下一页,可继续调用 getDeviceList(familyId, nextPageToken, …)。 } }, onError: (errorCode, errorMessage) => { // 查询设备列表失败,请根据错误码和错误信息处理。 }})
七、监听设备上下线
设备的在线状态变更通过 TXIoTEngine 的推送回调 onReceivePushMessage 下发。当 TXIoTPushMessage.type 为设备状态变更时,可从 subType 区分上线与下线,从 deviceId 定位具体设备。
注意:说明:
注意:上一节 getDeviceList 返回的 status.isOnline 只是查询时刻的快照。要让设备列表的在线状态保持实时准确,需要同时注册推送回调,在收到通知后按 deviceId 局部刷新对应条目,而不是轮询列表接口。
AndroidiOS小程序鸿蒙iotEngine.addListener(new TXIoTEngineListener() { @Override public void onReceivePushMessage(TXIoTPushMessage pushMessage) { if (pushMessage.type != TXIoTPushMessageType.STATUS_CHANGE) { return; } TXIoTDeviceId deviceId = pushMessage.deviceId; if (pushMessage.subType == TXIoTPushMessageSubType.ONLINE) { // 设备上线,按 deviceId 局部刷新列表项。 } else if (pushMessage.subType == TXIoTPushMessageSubType.OFFLINE) { // 设备离线。 } }});// 注册代理:[[TXIoTEngine getInstance] addDelegate:self];- (void)onReceivePushMessage:(TXIoTPushMessage *)pushMessage { if (pushMessage.type != TXIoTPushMessageTypeStatusChange) { return; } TXIoTDeviceId *deviceId = pushMessage.deviceId; if (pushMessage.subType == TXIoTPushMessageSubTypeOnline) { // 设备上线,按 deviceId 局部刷新列表项。 } else if (pushMessage.subType == TXIoTPushMessageSubTypeOffline) { // 设备离线。 }}const { TXIoTPushMessageType, TXIoTPushMessageSubType } = requirePlugin('tx-iot-sdk');iotEngine.addListener({ onReceivePushMessage(pushMessage) { if (pushMessage.type !== TXIoTPushMessageType.StatusChange) { return; } const deviceId = pushMessage.deviceId; if (pushMessage.subType === TXIoTPushMessageSubType.Online) { // 设备上线,按 deviceId 局部刷新列表项。 } else if (pushMessage.subType === TXIoTPushMessageSubType.Offline) { // 设备离线。 } },});import { TXIoTEngineListener, TXIoTPushMessageType, TXIoTPushMessageSubType } from '@tencentcloud/liteavsdk_iot'const engineListener: TXIoTEngineListener = { onReceivePushMessage: (pushMessage) => { if (pushMessage.type !== TXIoTPushMessageType.STATUS_CHANGE) { return } const deviceId = pushMessage.deviceId if (pushMessage.subType === TXIoTPushMessageSubType.ONLINE) { // 设备上线,按 deviceId 局部刷新列表项。 } else if (pushMessage.subType === TXIoTPushMessageSubType.OFFLINE) { // 设备离线。 } }}iotEngine.addListener(engineListener)
八、错误处理
设备绑定与分享相关的常见错误码如下,完整定义参见 TXIoTErrorCode。
AndroidiOS小程序鸿蒙
| 错误码 | 说明 | 建议处理方式 |
| ERR_INVALID_PARAMETER | 参数不合法。 | 检查 familyId、productId、deviceName、Token、别名是否为空。 |
| ERR_INVALID_ACCESS_TOKEN | 登录凭证无效。 | 重新登录 SDK,并重新获取管理对象。 |
| ERR_UNAUTHORIZED_OPERATION | 当前用户无权限。 | 确认当前用户是该家庭的成员,且对目标设备有管理权限;被分享用户不能解绑设备或修改设备信息。 |
| ERR_DEVICE_BOUND | 设备已绑定。 | 设备已绑定到其他家庭或账号,需先由原绑定方解绑。 |
| ERR_CAN_NOT_BIND_SAME_FAMILY | 不能重复绑定到同一家庭。 | 设备已在当前家庭中,直接使用即可,无需重复绑定。 |
| ERR_BIND_TOKEN_NOT_EXIST | 绑定 Token 不存在。 | 向业务后台重新申请设备绑定签名。 |
| ERR_BIND_TOKEN_IS_EXPIRED | 绑定 Token 已过期。 | 重新申请签名后立即绑定,不要缓存签名。 |
| ERR_BIND_TOKEN_NO_PAIR_WITH_DEVICE | 绑定 Token 与设备不匹配。 | 确认签名对应的设备与传入的 deviceId 一致。 |
| ERR_FAMILY_DEVICE_LIMIT_EXCEEDED | 家庭设备数量达到上限。 | 解绑无用设备,或将设备绑定到其他家庭。 |
| ERR_FAMILY_NOT_EXIST | 家庭不存在。 | 重新查询家庭列表,确认 familyId 有效。 |
| ERR_PRODUCT_NOT_EXISTERR_DEVICE_NOT_EXIST | 产品或设备不存在。 | 检查 productId 与 deviceName 是否正确。 |
| ERR_DEVICE_OFFLINE | 设备离线。 | 引导用户检查设备网络状态后重试。 |
| ERR_RATE_LIMITED | 请求被限频。 | 降低调用频率,避免高频轮询设备列表。 |
| 错误码 | 说明 | 建议处理方式 |
| TXIoTErrorCodeInvalidParameter | 参数不合法。 | 检查 familyId、productId、deviceName、Token、别名是否为空。 |
| TXIoTErrorCodeInvalidAccessToken | 登录凭证无效。 | 重新登录 SDK,并重新获取管理对象。 |
| TXIoTErrorCodeUnauthorizedOperation | 当前用户无权限。 | 确认当前用户是该家庭的成员,且对目标设备有管理权限;被分享用户不能解绑设备或修改设备信息。 |
| TXIoTErrorCodeDeviceBound | 设备已绑定。 | 设备已绑定到其他家庭或账号,需先由原绑定方解绑。 |
| TXIoTErrorCodeCanNotBindSameFamily | 不能重复绑定到同一家庭。 | 设备已在当前家庭中,直接使用即可,无需重复绑定。 |
| TXIoTErrorCodeBindTokenNotExist | 绑定 Token 不存在。 | 向业务后台重新申请设备绑定签名。 |
| TXIoTErrorCodeBindTokenIsExpired | 绑定 Token 已过期。 | 重新申请签名后立即绑定,不要缓存签名。 |
| TXIoTErrorCodeBindTokenNoPairWithDevice | 绑定 Token 与设备不匹配。 | 确认签名对应的设备与传入的 deviceId 一致。 |
| TXIoTErrorCodeFamilyDeviceLimitExceeded | 家庭设备数量达到上限。 | 解绑无用设备,或将设备绑定到其他家庭。 |
| TXIoTErrorCodeFamilyNotExist | 家庭不存在。 | 重新查询家庭列表,确认 familyId 有效。 |
| TXIoTErrorCodeProductNotExist / TXIoTErrorCodeDeviceNotExist | 产品或设备不存在。 | 检查 productId 与 deviceName 是否正确。 |
| TXIoTErrorCodeDeviceOffline | 设备离线。 | 引导用户检查设备网络状态后重试。 |
| TXIoTErrorCodeRateLimited | 请求被限频。 | 降低调用频率,避免高频轮询设备列表。 |
| 错误码 | 说明 | 建议处理方式 |
| InvalidParameter | 参数不合法。 | 检查 familyId、productId、deviceName、Token、别名是否为空。 |
| InvalidAccessToken | 登录凭证无效。 | 重新登录 SDK,并重新获取管理对象。 |
| UnauthorizedOperation | 当前用户无权限。 | 确认当前用户是该家庭的成员,且对目标设备有管理权限;被分享用户不能解绑设备或修改设备信息。 |
| DeviceBound | 设备已绑定。 | 设备已绑定到其他家庭或账号,需先由原绑定方解绑。 |
| CanNotBindSameFamily | 不能重复绑定到同一家庭。 | 设备已在当前家庭中,直接使用即可,无需重复绑定。 |
| BindTokenNotExist | 绑定 Token 不存在。 | 向业务后台重新申请设备绑定签名。 |
| BindTokenIsExpired | 绑定 Token 已过期。 | 重新申请签名后立即绑定,不要缓存签名。 |
| BindTokenNoPairWithDevice | 绑定 Token 与设备不匹配。 | 确认签名对应的设备与传入的 deviceId 一致。 |
| FamilyDeviceLimitExceeded | 家庭设备数量达到上限。 | 解绑无用设备,或将设备绑定到其他家庭。 |
| FamilyNotExist | 家庭不存在。 | 重新查询家庭列表,确认 familyId 有效。 |
| ProductNotExistDeviceNotExist | 产品或设备不存在。 | 检查 productId 与 deviceName 是否正确。 |
| DeviceOffline | 设备离线。 | 引导用户检查设备网络状态后重试。 |
| RateLimited | 请求被限频。 | 降低调用频率,避免高频轮询设备列表。 |
| 错误码 | 说明 | 建议处理方式 |
| ERR_INVALID_PARAMETER | 参数不合法。 | 检查 familyId、productId、deviceName、Token、别名是否为空。 |
| ERR_INVALID_ACCESS_TOKEN | 登录凭证无效。 | 重新登录 SDK,并重新获取管理对象。 |
| ERR_UNAUTHORIZED_OPERATION | 当前用户无权限。 | 确认当前用户是该家庭的成员,且对目标设备有管理权限;被分享用户不能解绑设备或修改设备信息。 |
| ERR_DEVICE_BOUND | 设备已绑定。 | 设备已绑定到其他家庭或账号,需先由原绑定方解绑。 |
| ERR_CAN_NOT_BIND_SAME_FAMILY | 不能重复绑定到同一家庭。 | 设备已在当前家庭中,直接使用即可,无需重复绑定。 |
| ERR_BIND_TOKEN_NOT_EXIST | 绑定 Token 不存在。 | 向业务后台重新申请设备绑定签名。 |
| ERR_BIND_TOKEN_IS_EXPIRED | 绑定 Token 已过期。 | 重新申请签名后立即绑定,不要缓存签名。 |
| ERR_BIND_TOKEN_NO_PAIR_WITH_DEVICE | 绑定 Token 与设备不匹配。 | 确认签名对应的设备与传入的 deviceId 一致。 |
| ERR_FAMILY_DEVICE_LIMIT_EXCEEDED | 家庭设备数量达到上限。 | 解绑无用设备,或将设备绑定到其他家庭。 |
| ERR_FAMILY_NOT_EXIST | 家庭不存在。 | 重新查询家庭列表,确认 familyId 有效。 |
| ERR_PRODUCT_NOT_EXIST / ERR_DEVICE_NOT_EXIST | 产品或设备不存在。 | 检查 productId 与 deviceName 是否正确。 |
| ERR_DEVICE_OFFLINE | 设备离线。 | 引导用户检查设备网络状态后重试。 |
| ERR_RATE_LIMITED | 请求被限频。 | 降低调用频率,避免高频轮询设备列表。 |
九、设备列表的在线状态不准确,需要自己轮询吗?
不需要。getDeviceList 返回的 TXIoTDeviceInfo.status.isOnline 是查询时刻的状态快照,SDK 已在内部自动补齐,无需业务层额外调用状态查询接口。要保持实时性,请注册 TXIoTEngine 的 onReceivePushMessage 回调,收到设备状态变更通知后按 deviceId 局部刷新对应列表项。高频轮询设备列表可能触发限频错误。
十、分页时 nextPageToken 可以自己拼接或跳页吗?
不可以。nextPageToken 是 SDK 内部维护的游标,其格式属于实现细节,未来可能变化。请始终把上一次回调返回的值原样传给下一次调用,直到返回空字符串。该游标也不能跨接口复用:getDeviceList 与查询共享设备的 getDeviceListSharedWithMe(参见 设备分享)各自维护独立的分页序列。
十一、接口参考
这篇涉及接口的完整定义参见:
·TXIoTDeviceManager(Android)
·TXIoTDeviceManager(iOS)
·TXIoTDeviceManager(小程序)
·TXIoTDeviceManager(鸿蒙)
·TXIoTEngineDef(数据结构与错误码)
把单台设备授权给其他账号,参见 设备分享。家庭、房间与家庭成员的管理,以及把设备加入或移出房间的操作,参见 家庭管理。设备绑定完成后,可继续接入 远程控制。