设备管理
包括设备绑定、设备解绑、设备列表、设备配网
// 导入设备绑定头文件
import { RTCXAddDeviceService, RTCXAddDeviceType, RTCXDeviceMgr, RTCXDeviceMgrTypes, RTCXThingModel, RTCXThingModelType, RTCXLogger } from 'rtcxsdk'
设备绑定
1. 无线绑定(如: 二维码、蓝牙等方式)
总体介绍参考设备绑定开发指南。
- 设备绑定流程
- App调用prepareAddDevice()方法后,当监听到EAddDeviceStatus.Prepared状态时可获取bindToken (有效时长30分钟)。
- 将bindToken传递给设备(二维码、蓝牙等),设备保证网络连通情况下,通过DeviceSDK进行设备绑定
- 海外设备绑定时,同时还需要将regionId传递给设备
- 在EAddDeviceStatus.Prepared状态下,App只需要调用startAddDevice()方法实时监听绑定状态回调即可(超时自行控制,最大超时依赖bindToken的有效时长)。
- startAddDevice()方法需要等添加状态为EAddDeviceStatus.Prepared时才可以调用,所以可以在 onAddDeviceStatus: ()=>void 回调状态为EAddDeviceStatus.Prepared调用开始添加方法
- 已配网设备(如4G、有线设备)无需处理步骤2,即不需要处理bindToken
- 设备绑定详细流程说明请点击
相关API如下:
export interface IAddStatusListener {
// 设备添加状态回调,回调过程中状态、数据、错误信息
onAddDeviceStatus: (status: EAddDeviceStatus, data: Object|undefined, error: BusinessError<void>|undefined)=>void
}
export interface IAddDeviceService {
/**
* 准备添加设备,成功才可以进行下一步
*
* @param addType 添加类型
*/
prepareAddDevice(addType: EAddDeviceType): void
/**
* 开始添加设备,成功才可以进行下一步
*
* @param addReq 配网模式对应入参
*/
startAddDevice(addReq: IAddDeviceReq): void
/**
* 停止添加设备
*
*/
stopAddDevice(): void
/**
* 注册设备添加状态回调监听
*
* @param listener 回调监听者
*/
registerStatusListener(listener: IAddStatusListener): void
/**
* 获取配网所需随机数
*/
getBindToken(): string
/**
获取当前regionId(海外设备配网需要)
*/
getRegionId(): number
}
调用示例如下:
- 注册监听,并准备绑定
statusListerner: RTCXAddDeviceType.IAddStatusListener = {
onAddDeviceStatus: (status: RTCXAddDeviceType.EAddDeviceStatus, data: Object|undefined, error: BusinessError<void>|undefined)=> {
RTCXLogger.i(this.TAG, `device add status: ${status}, ${data}, ${error}`)
if (status === RTCXAddDeviceType.EAddDeviceStatus.Prepared) {
const bindToken = RTCXAddDeviceService.getBindToken()
RTCXLogger.i(this.TAG, `get bind token: ${bindToken}`)
} else if (status === RTCXAddDeviceType.EAddDeviceStatus.Success) {
showToast(this,'绑定成功')
this.pathStack.pop(true)
} else if (status === RTCXAddDeviceType.EAddDeviceStatus.Failed) {
showToast(this,'绑定失败')
}
}
}
RTCXAddDeviceService.registerStatusListener(this.statusListerner)
RTCXAddDeviceService.prepareAddDevice(RTCXAddDeviceType.EAddDeviceType.QR)
- 开始绑定
const req: RTCXAddDeviceType.QRAddDeviceReq = {addDeviceType: RTCXAddDeviceType.EAddDeviceType.QR}
RTCXAddDeviceService.startAddDevice(req)
- 结束绑定(该方法可以不调用,内部会自动处理)
RTCXAddDeviceService.stopAddDevice()
2. 有线绑定(如: 4G、有线设备)
- 与上面正常设备绑定流程类似,特殊之处是不需要向设备传递bindToken,而是直接调用WRAddDeviceReq有线添加的方式即可完成绑定(如:App可以扫描设备上的二维码获取productKey和deviceName,然后调用startAddDevice()方法绑定,同时监听绑定状态回调)
// App扫描设备二维码绑定
const req: RTCXAddDeviceType.WRAddDeviceReq = {productKey: 'xxx', deviceName: 'xxx', addDeviceType: RTCXAddDeviceType.EAddDeviceType.WR}
RTCXAddDeviceService.startAddDevice(req)
设备解绑
/**
* 解绑用户的设备
* @param iotId - 设备ID
*/
export async function deviceUnbind(iotId: string): Promise<void>
调用示例如下:
RTCXDeviceMgr.deviceUnbind(deviceInfo.iotId)
设备列表
/**
* 设备列表
* @param deviceListReq - 设备列表请求参数
* @returns
* @public
*/
export async function getDeviceList(deviceListReq: IDeviceListReq): Promise<IDeviceListResp>
/**
* 获取网关子设备
* @param subListReq - 获取网关子设备列表请求参数
* @returns
* @public
*/
export async function getGatewaySubList(subListReq: GatewaySubListReq): Promise<IDeviceListResp>
调用示例如下:
const reqParams: RTCXDeviceMgrTypes.IDeviceListReq = {
pageNo: 1,
pageSize: 100
}
try {
const deviceListRsp = await RTCXDeviceMgr.getDeviceList(reqParams)
this.deviceList = deviceListRsp.data
} catch (e) {
showToast(this, `获取设备列表失败${e}`)
}
获取设备状态
/**
* 获取设备状态
* @param iotId - 设备ID
* @returns 设备状态 DeviceStatusEnum
* @public
*/
export async function getDeviceStatus(iotId: string): Promise<DeviceStatusEnum>
调用示例如下:
try {
const status = await RTCXDeviceMgr.getDeviceStatus('iotId')
} catch (e) {
showToast(this, `获取设备状态失败${e}`)
}
设备缩略图
通过物模型服务获取设备缩略图
this.thingModel = new RTCXThingModel(PlayDeviceInfo)
try {
const result = await this.thingModel.invokeService<IDevPicture>('GetDevPicture',{'definition': 1, "picType": "jpg"})
if (result) {
const rsp = result as RTCXThingModelType.InvokeServiceRsp<IDevPicture>
if (rsp.code == 200) {
const devPicture = rsp.data as IDevPicture
RTCXLogger.i(TAG,`设备缩略图:${thingM.deviceInfo.iotId}, ${devPicture.downloadUrl}`)
this.deviceThumnails[thingM.deviceInfo.iotId] = devPicture
} else {
showToast(this, `获取缩略图失败`)
}
}
} catch (e) {
showToast(this, `获取缩略图失败${e}`)
}
设备配置
//导入模块
import { RTCXDeviceEntityMgr, RTCXDeviceMgrTypes} from 'rtcxsdk'
/**
* 设备配置项管理
* @public
*/
export declare const RTCXDeviceEntityMgr: {
/**
* 设置设备配置项(单个)
*
* @param iotId - 设备ID
* @param entityEnum - 设置项
* @param value - 设置内容
*/
setDeviceEntity(iotId: string, entityEnum: DeviceEntityEnum, value: string): void | Promise<IGatewayResponse>;
/**
* 获取指定ID配置
*
* @param iotId - 设备ID
* @param entityEnum - 配置ID
*/
getDeviceEntity(iotId: string, entityEnum: DeviceEntityEnum): Promise<string|undefined>;
};
配置项 DeviceEntityEnum
/**
* 设备配置
* @public
*/
declare enum DeviceEntityEnum {
/**
* 消息推送设置
*/
MSG_PUSH_SETTING = 1003,
}
消息通知计划
/**
* 推送设置
* @public
*/
declare interface MsgPushSetting {
/** 推送关闭计划列表 */
sch: MsgPushSettingSchedule[];
/** 是否接收推送消息 */
sta: number;
/** 是否推送图片 */
img: number;
/** 推送间隔 单位:秒 */
interval: number;
}
/**
* 推送计划
* @public
*/
declare interface MsgPushSettingSchedule {
/** 开始时间,utc时间,秒 */
start: number;
/** 结束时间,utc时间,秒 (end时间应始终大于start时间,跨天的时候,end为第二天的时间) */
end: number;
/** 对应的值仅在"rpt"为2时有效,存在重复设置值:SUN:0x0001, MON:0x0002, TUE:0x0004, WED:0x0008, THU:0x0010, FRI:0x0020, SATU:0x0040,支持按位或操作 */
wk: number;
/** 重复类型,0:执行一次 1:每天重复 2:每周重复 3:每月重复 (目前支持1和2) */
rpt: number;
/** 推送关闭计划状态 1表示状态开,0表示状态关 */
sta: number;
}
获取设备配置
/**
* 获取指定ID配置
*
* @param iotId - 设备ID
* @param entityEnum - 配置ID
*/
getDeviceEntity(iotId: string, entityEnum: DeviceEntityEnum): Promise<string|undefined>;
示例
import { RTCXDeviceEntityMgr, RTCXDeviceMgrTypes} from 'rtcxsdk'
async function (iotId: string){
const result = await RTCXDeviceEntityMgr.getDeviceEntity(
iotId,
RTCXDeviceMgrTypes.DeviceEntityEnum.MSG_PUSH_SETTING,
);
if(result) {
const msgNotifySetting = JSON.parse(result) as RTCXDeviceMgrTypes.MsgPushSetting
}
}
设置设备配置
/**
* 设置设备配置项(单个)
*
* @param iotId - 设备ID
* @param entityEnum - 设置项
* @param value - 设置内容
*/
setDeviceEntity(iotId: string, entityEnum: DeviceEntityEnum, value: string): void | Promise<IGatewayResponse>;
示例
import { RTCXDeviceEntityMgr, RTCXDeviceMgrTypes} from 'rtcxsdk'
const msgNotifySetting: RTCXDeviceMgrTypes.MsgPushSetting = {
....
}
async function (iotId: string){
const result = await RTCXDeviceEntityMgr.setDeviceEntity(
iotId,
RTCXDeviceMgrTypes.DeviceEntityEnum.MSG_PUSH_SETTING,
JSON.stringify(msgNotifySetting)
);
}
云台预置位
import {
RTCXDeviceMgrTypes,
RTCXPTZPresetMgr,
RTCXThingModelTypes,
} from 'rtcxsdk'
收藏常看的云台位置,方便快速转动设备镜头至相应位置
/**
* 预置位管理
* @public
*/
export declare class RTCXPTZPresetMgr {
private presetList;
private iotId;
/**
*
* @param iotId - 设备ID
*
*/
constructor(iotId: string);
/**
* 获取设备预置位列表
*
*/
getPresetList(): Promise<PTZPresetRspItem[]>;
/**
* 添加预置位
*
* @param reqParams - [PTZPresetReq]
*/
addPreset(reqParams: PTZPresetReq): Promise<PTZPresetRspItem>;
/**
* 删除预置位
*
* @param item - 列表返回的预置位项[PTZPresetRspItem]
*/
removePreset(item: PTZPresetRspItem): Promise<boolean>;
/**
* 更新预置位,一般用于修改名称
*
* @param item - 列表返回的预置位项[PTZPresetRspItem]
*/
updatePreset(item: PTZPresetRspItem): Promise<boolean>;
}
构建对象
import {
RTCXDeviceMgrTypes,
RTCXPTZPresetMgr,
RTCXThingModelTypes,
} from 'rtcxsdk'
const iotId: string = ''
let presetMgr: RTCXPTZPresetMgr = new RTCXPTZPresetMgr(iotId)
获取设备预置位列表
import {
RTCXPTZPresetMgr,
RTCXThingModelTypes,
} from 'rtcxsdk'
let presetMgr: RTCXPTZPresetMgr | null = null
// 加载预置位列表
async function loadPresetList(iotId: string) {
try {
if (!presetMgr) {
presetMgr = new RTCXPTZPresetMgr(iotId)
}
const presetList = await presetMgr.getPresetList()
} catch (error) {
console.error('加载预置位列表失败:', error)
}
}
RTCXThingModelTypes.PTZPresetRspItem
/**
* 预置位列表
*
* @public
*/
declare interface PTZPresetRspItem {
/**
* 预置位信息
*/
readonly ptz: string;
/**
* 预置位名称
*/
name?: string;
/**
* 预置位图片ID
*/
readonly picId?: string;
/**
* 预置位图片地址
*/
readonly picUrl?: string;
}
添加预置位
-
通过物模型或其它途径获取设备当前云台位置信息
-
用户输入位置名称
-
获取设备当前位置画面(可截图播放器当前直播画面)
import {
RTCXPTZPresetMgr,
RTCXThingModelTypes,
} from 'rtcxsdk'
const ptzPosition = "获取到的当前云台位置信息"
const presetName ="常看位置"
async function handleAddPreset() {
try {
await presetMgr.addPreset({
ptz: ptzPosition,
name: presetName,
})
console.info('添加预置位成功')
} catch (error) {
console.error('添加预置位失败:', error)
}
}
RTCXThingModelTypes.PTZPresetReq
/**
* 预置位保存参数 预置位名称与截图至少有一个
*
* @public
*/
declare interface PTZPresetReq {
/**
* 预置位信息
*/
ptz: string;
/**
* 预置位名称 可选
*/
name?: string;
/**
* 预置位截图 可选 (暂不支持)
*/
img?: Blob | File;
}
删除预置位
传入列表中获取数据,删除指定预置位
import {
RTCXPTZPresetMgr,
RTCXThingModelTypes,
} from 'rtcxsdk'
async function handleDeletePreset(item: RTCXThingModelTypes.PTZPresetRspItem) {
await presetMgr.removePreset(item)
await loadPresetList()
} catch (error) {
console.error('删除预置位失败:', error)
}
}
修改预置位名称
更新预置位名称 如果ptz信息或图片变化请删除重新添加
import {
RTCXPTZPresetMgr,
RTCXThingModelTypes,
} from 'rtcxsdk'
const presetName = "新的位置名称"
async function handleSaveEdit(item: RTCXThingModelTypes.PTZPresetRspItem) {
try {
const newItem: RTCXThingModelTypes.PTZPresetRspItem = {
ptz: item.ptz,
picId: item.picId,
picUrl: item.picUrl,
name: presetName,
}
await presetMgr.updatePreset(newItem)
console.info('修改预置位名称成功')
await loadPresetList()
} catch (error) {
console.error('修改预置位名称失败:', error)
}
}
低功耗设备唤醒
低功耗设备由于实现方案的不同,播放的方式有所区别,以下是设备DeviceSDK中定义的几种方式
/**
* XS_IOT_WORK_MODE_LOWPOWER_PROXY = 1, //低功耗设备,代理模式,注意只有支持低功耗保活连接的设备才需要设置此模式,设置了此模式之后必须和wifi 芯片上的 lowPwrSDK配合才能工作
* XS_IOT_WORK_MODE_LONG_TIME_POWER = 2 , //长上电设备,正常链接模式(iotsdk和streamsdk运行在一个进程中)
* XS_IOT_WORK_MODE_LOWPOWER_SEPARATE = 3, //低功耗设备,iotsdk和streamsdk分离模式,iotsdk运行在wifi或4g芯片上维持信令长连接,streamsdk运行在主控芯片上
* XS_IOT_WORK_MODE_LOWPOWER_KEEPLIVE = 4, //低功耗设备,iotsdk和streamsdk运行在主控芯片上,wifi或4g芯片上仅维持简单心跳保活,支持远程唤醒
*/
工作模式为1,3的低功耗设备低功耗设备与常长电设备(工作模式为2)播放流程一致,以下示例仅为工作模式4的特殊播放流程
- 先唤醒设备(即使设备列表获取状态为上线,也需要唤醒一下设备)
- 监听设备上线
- 调用播放接口 (请务必在设备上线后调用)
唤醒接口
/**
* 低功耗设备唤醒
* @public
*/
declare class DeviceWakeUpMgr {
/**
*
* @param iotId - 设备唯一ID
* @param wakeupTimeout - 超时时长 单位:毫秒 默认30_000
* @param checkStatusInterval - 轮询设备状态频率 单位:毫秒 默认1000
*/
constructor(iotId: string, wakeupTimeout?: number, checkStatusInterval?: number);
/**
* 唤醒设备,等待设备上线
*/
wakeup(): Promise<void>;
/**
* 结束唤醒流程
*/
stop(): void;
}
调用示例如下:
// 导入设备唤醒头文件
import { RTCXLogger, RTCXDeviceMgrTypes, RTCXDeviceWakeUpMgr } from 'rtcxsdk'
let deviceWakeUpMgr: RTCXDeviceWakeUpMgr | null = null
async function playDevice(deviceInfo: RTCXDeviceMgrTypes.PlayDeviceInfo){
//判断设备是否是低功耗设备
if (
deviceInfo.deviceWorkMode ===
RTCXDeviceMgrTypes.DeviceWorkModeEnum.XS_IOT_WORK_MODE_LOWPOWER_KEEPLIVE
) {
deviceWakeUpMgr?.stop()
deviceWakeUpMgr = new RTCXDeviceWakeUpMgr(deviceInfo.iotId,30_000, 1000)
try{
await deviceWakeUpMgr.wakeup()
}catch(e){
//处理超时等异常情况,可根据业务自行处理
//示例:重新走一次唤醒流程
playDevice(deviceInfo)
return
}
}
initPlayer(deviceInfo) //见基础播放示例
}
设备配网
设备需要配置网络时,App 通过各种方式将网络信息传递给设备。
AP配网
设备 AP 配网时,待设备重置后进入待连接状态,手机连接设备热点,通过 App 传输 Wi-Fi 的 SSID 和密码,使设备加入目标网络。
// 导入 AP 配网头文件
import { RTCXAPLinkService, RTCXAPLinkType } from 'rtcxsdk'
AP 配网调用接口
export type APLinkConnectCallback =
(isConnect: boolean, error: BusinessError<void> | undefined) => void
export type APLinkResponseCallback =
(success: boolean, response: Object | undefined, error: BusinessError<void> | undefined) => void
export interface IAPLinkService {
/**
* 开始连接AP设备
*
* @param req 连接参数对象(可设置host、port和超时时间)
* @param connectCallback 连接结果回调
*/
startConnect(req: IAPLinkConnectReq, connectCallback: APLinkConnectCallback): void
/**
* 断开连接
*/
disconnect(): void
/**
* 发送配网信息
*
* @param req 配网参数对象,需要传ssid和pwd
* @param responseCallback 配网结果回调
*/
sendNetworking(req: IAPLinkNetworkingReq, responseCallback: APLinkResponseCallback): void
}
请求参数
// AP连接请求
export interface IAPLinkConnectReq {
// 超时时间,单位:秒,默认60s,可选
timeout?: number
// 连接host,可选,默认192.168.10.1
host?: string
// 连接端口,可选,默认58888
port?: number
}
// AP配网信息
export interface IAPLinkNetworkingReq {
// 超时时间,单位:秒,默认60s,可选
timeout?: number
// WiFi名称,必选
ssid: string
// WiFi密码,必选
pwd: string
}
错误码
// AP配网错误码
export enum EAPLinkError {
// 未使用
NO_ERROR = 0,
// 无效配置
BAD_CONFIG_ERROR = 1,
// 无效参数
BAD_PARAM_ERROR = 2,
// 连接超时
CONNECT_TIMEOUT_ERROR = 3,
// 读取超时
READ_TIMEOUT_ERROR = 4,
// 写入超时
WRITE_TIMEOUT_ERROR = 5,
// 读取达到最大长度仍未完成
READ_MAXED_OUT_ERROR = 6,
// 远端关闭连接
CLOSED_ERROR = 7,
// 其他错误
OTHER_ERROR = 8
}
// AP设备返回的配网结果码
export enum EAPLinkConfigCode {
// 配网成功
SUCCESS = 1000,
// 缺少ssid字段
ERR_MISSING_SSID = 1001,
// JSON格式无效
ERR_INVALID_JSON = 1002
}
调用示例如下:
- 准备设备绑定,引导用户连接设备热点
应用通常无法直接切换 Wi-Fi,建议引导用户前往系统 Wi-Fi 设置连接设备热点,连接成功后再调用 AP 配网接口。
进入 AP 配网流程时,先准备添加设备(绑定状态监听请在发起绑定前注册):
RTCXAddDeviceService.prepareAddDevice(RTCXAddDeviceType.EAddDeviceType.AP)
用户连接上设备热点后,开始连接 AP 设备:
const connectReq: RTCXAPLinkType.IAPLinkConnectReq = {
timeout: 20 // 连接超时,单位:秒
}
RTCXAPLinkService.startConnect(connectReq,
(isConnect: boolean, error: BusinessError<void> | undefined) => {
if (!isConnect) {
// 连接失败,error.code 见 EAPLinkError
showToast(`连接设备热点失败:${error?.code} ${error?.message}`)
return
}
// 连接成功,发送Wi-Fi信息
})
- 发送 Wi-Fi 名称和密码
const netReq: RTCXAPLinkType.IAPLinkNetworkingReq = {
ssid: 'wifi名称',
pwd: 'wifi密码',
timeout: 60 // 等待设备响应的超时时间,单位:秒
}
RTCXAPLinkService.sendNetworking(netReq,
(success: boolean, response: Object | undefined,
error: BusinessError<void> | undefined) => {
if (!success) {
// 设备返回1001/1002等结果码时,error.code为 EAPLinkConfigCode
showToast(`配网失败:${error?.code} ${error?.message}`)
return
}
// 设备已接收Wi-Fi信息,发起绑定
})
发送 Wi-Fi 名称和密码后,设备连接上目标 Wi-Fi 会主动断开热点;App 会短暂断开网络,请等待系统重新连接其他网络,绑定结果以 onAddDeviceStatus 回调为准。
- 发起设备绑定
配网信息发送成功后,按通用设备绑定流程调用 startAddDevice,AP 方式使用 APAddDeviceReq:
const addReq: RTCXAddDeviceType.APAddDeviceReq = {
addDeviceType: RTCXAddDeviceType.EAddDeviceType.AP,
productKey: '设备的productKey',
deviceName: '设备的deviceName'
}
RTCXAddDeviceService.startAddDevice(addReq)
绑定结果通过 registerStatusListener 的 onAddDeviceStatus 回调监听,详见设备绑定。建议业务层增加整体超时控制(如 120 秒),避免设备未上线时流程一直等待。
- 断开连接
RTCXAPLinkService.disconnect()
页面销毁或流程结束时调用,释放 TCP 连接资源;若此时仍有等待中的连接或配网请求,会以 EAPLinkError.CLOSED_ERROR 失败回调。
- 连接失败的错误码来自
EAPLinkError,如CONNECT_TIMEOUT_ERROR(3)、CLOSED_ERROR(7)、OTHER_ERROR(8)。 - 设备返回的配网结果码来自
EAPLinkConfigCode:1000 成功、1001 缺少 ssid、1002 JSON 格式无效;等待设备响应超时返回EAPLinkError.READ_TIMEOUT_ERROR(4)。 - 配网成功时
success为true,response为设备返回的 JSON 对象;设备返回未知结果码时success为false,error可能为空,需要结合response判断。 - 重复调用
startConnect会先释放旧连接,旧连接的请求不会收到回调。
蓝牙配网
设备蓝牙配网时,待设备重置后进入待配网状态,App 通过 BLE(低功耗蓝牙)扫描并连接设备,向设备发送 Wi-Fi 名称和密码,设备加入目标网络后由云端完成设备绑定。
// 导入蓝牙配网头文件
import { RTCXBluetoothLinkService, RTCXBluetoothLinkType } from 'rtcxsdk'
蓝牙配网调用接口
export type BluetoothAuthorizeStatusBlock =
(authorizeStatus: EBluetoothAuthorizeStatus) => void
export type BluetoothDeviceResponseHandler =
(success: boolean, response: Object | undefined, error: BusinessError<void> | undefined) => void
export interface IBluetoothLinkService {
/**
* 检查蓝牙授权状态
*
* @param authorizeStatusBlock 授权状态回调
*/
checkBluetoothAuthorizeStatus(authorizeStatusBlock: BluetoothAuthorizeStatusBlock): void
/**
* 开始扫描蓝牙设备
*
* @param req 扫描参数对象(可设置名称关键字和超时时间)
* @param scanResultBlock 扫描结果回调
*/
startScanBluetoothDevices(req: IBluetoothLinkScanReq,
scanResultBlock: BluetoothDeviceResponseHandler): void
/**
* 停止扫描蓝牙设备
*/
stopScanBluetoothDevices(): void
/**
* 连接蓝牙设备
*
* @param req 连接参数对象,需要传sn
* @param connectResultBlock 连接结果回调
*/
connectBluetoothDevice(req: IBluetoothLinkNetworkingReq,
connectResultBlock: BluetoothDeviceResponseHandler): void
/**
* 连接设备并发送配网信息,不需要再单独调用连接和配网方法
*
* @param req 请求参数对象,需要传sn和content
* @param networkingResultBlock 配网结果回调
*/
connectCombineNetworkingBluetoothDevice(req: IBluetoothLinkNetworkingReq,
networkingResultBlock: BluetoothDeviceResponseHandler): void
/**
* 向已连接的设备发送配网信息
*
* @param req 配网参数对象,需要传content
* @param networkingResultBlock 配网结果回调
*/
networkingBluetoothDevice(req: IBluetoothLinkNetworkingReq,
networkingResultBlock: BluetoothDeviceResponseHandler): void
/**
* 获取设备信息
*
* @param req 请求参数对象,需要传sn
* @param devInfoResultBlock 设备信息结果回调
*/
getBluetoothDeviceInfo(req: IBluetoothLinkQueryDeviceInfoReq,
devInfoResultBlock: BluetoothDeviceResponseHandler): void
/**
* 获取设备WiFi列表
*
* @param req 请求参数对象,需要传sn
* @param wifiListResultBlock WiFi列表结果回调
*/
getBluetoothDeviceWifiList(req: IBluetoothLinkQueryWifiReq,
wifiListResultBlock: BluetoothDeviceResponseHandler): void
}
请求与响应参数
export interface IBluetoothLinkModel {
// 超时时间,单位:秒,默认120s,可选
timeout?: number
}
// 扫描请求
export interface IBluetoothLinkScanReq extends IBluetoothLinkModel {
// 蓝牙设备名称包含的关键字,如:XS_[控制台产品型号]_、XSDEV_
namePrefixs?: string[]
}
// 连接/配网请求
export interface IBluetoothLinkNetworkingReq extends IBluetoothLinkModel {
// 蓝牙设备SN,连接设备时必填,取扫描结果中的sn
sn?: string
// 配网信息,发送配网信息时必填
content?: string
}
// 获取设备信息请求
export interface IBluetoothLinkQueryDeviceInfoReq extends IBluetoothLinkNetworkingReq {
}
// 获取WiFi列表请求
export interface IBluetoothLinkQueryWifiReq extends IBluetoothLinkNetworkingReq {
}
// 扫描到的蓝牙设备信息
export interface IBluetoothLinkDevInfoRsp extends IBluetoothLinkModel {
// 蓝牙设备名称
name?: string
// 蓝牙设备SN(即BLE deviceId)
sn?: string
}
// 设备返回的响应数据
export interface IBluetoothLinkResponseData {
// 设备mac
mac: string
// 设备返回的指令
cmd: number
// 设备返回的原始内容
data: string
}
蓝牙授权状态与错误码
// 蓝牙授权状态
export enum EBluetoothAuthorizeStatus {
// 未知
UNKNOWN = -1,
// 该设备不支持蓝牙低功耗
UNSUPPORTED = 0,
// 该应用无权使用蓝牙低功耗
UNAUTHORIZED = 1,
// 蓝牙关闭
POWERED_OFF = 2,
// 蓝牙打开
POWERED_ON = 3
}
// 蓝牙配网错误码
export enum EBluetoothLinkError {
// 扫描启动失败
REQUEST_AUTH_CODE_FAIL = 0x01,
// 蓝牙连接失败
CONNECT_FAIL = 0x02,
// 蓝牙断开连接
DISCONNECT = 0x03,
// 蓝牙不支持写入
WRITE_UNSUPPORTED = 0x04,
// 蓝牙消息发送失败
SEND_MESSAGE_FAIL = 0x05,
// 蓝牙消息解码失败
MESSAGE_ANALYSIS_FAIL = 0x06,
// 请求超时
LINK_TIMEOUT = 0x07
}
蓝牙配网依赖系统 BLE 能力,接入前需要:
- 在
module.json5中声明ohos.permission.ACCESS_BLUETOOTH权限; - 在发起扫描前动态申请该权限;
- 确保手机蓝牙已打开。
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.ACCESS_BLUETOOTH",
"reason": "$string:bluetooth_permission_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}
]
}
}
import { abilityAccessCtrl, common } from '@kit.AbilityKit'
async function requestBluetoothPermission(context: common.UIAbilityContext): Promise<boolean> {
const atManager = abilityAccessCtrl.createAtManager()
const result = await atManager.requestPermissionsFromUser(context, ['ohos.permission.ACCESS_BLUETOOTH'])
return result.authResults.every((authResult: number) => authResult === 0)
}
调用示例如下:
- 准备添加设备并注册绑定监听
进入蓝牙配网流程时,先注册绑定状态监听并准备添加设备(绑定状态监听建议在发起绑定前注册,详见设备绑定):
RTCXAddDeviceService.registerStatusListener(addStatusListener)
RTCXAddDeviceService.prepareAddDevice(RTCXAddDeviceType.EAddDeviceType.BT)
- 申请权限并检查蓝牙状态
const granted = await requestBluetoothPermission(context)
if (!granted) {
showToast('请先授予蓝牙权限')
return
}
RTCXBluetoothLinkService.checkBluetoothAuthorizeStatus(
(status: RTCXBluetoothLinkType.EBluetoothAuthorizeStatus) => {
if (status !== RTCXBluetoothLinkType.EBluetoothAuthorizeStatus.POWERED_ON) {
showToast('请打开手机蓝牙后重试')
return
}
startScan()
})
checkBluetoothAuthorizeStatus 会持续监听蓝牙开关状态,用户打开或关闭蓝牙时回调会再次触发,业务需处理重复回调。
- 扫描设备
const scanReq: RTCXBluetoothLinkType.IBluetoothLinkScanReq = {
namePrefixs: ['XS'], // 设备名称包含的关键字,需按实际设备名称设置
timeout: 30 // 扫描超时,单位:秒
}
RTCXBluetoothLinkService.startScanBluetoothDevices(scanReq,
(success: boolean, response: Object | undefined,
error: BusinessError<void> | undefined) => {
if (!success) {
// 扫描失败或超时,error.code 见 EBluetoothLinkError
showToast(`扫描失败:${error?.code} ${error?.message}`)
return
}
// response 为累计扫描到的设备列表
const devices = response as RTCXBluetoothLinkType.IBluetoothLinkDevInfoRsp[]
// 刷新页面设备列表
})
- 扫描结果回调可能多次触发,
response为当前累计扫描到的设备列表,而非增量数据。 - 未设置
timeout时默认 120 秒;扫描到第一台设备后会停止超时计时,但扫描会持续进行,需要业务在用户选择设备或离开页面时主动调用stopScanBluetoothDevices。 namePrefixs必须设置且不能为空数组,匹配方式为设备名称包含任一关键字。- 扫描结果中的
sn即 BLE deviceId,后续连接、获取信息、配网均使用该值。
- 连接设备
// 用户选择设备后停止扫描
RTCXBluetoothLinkService.stopScanBluetoothDevices()
const connectReq: RTCXBluetoothLinkType.IBluetoothLinkNetworkingReq = {
sn: device.sn,
timeout: 30 // 连接超时,单位:秒
}
RTCXBluetoothLinkService.connectBluetoothDevice(connectReq,
(success: boolean, response: Object | undefined,
error: BusinessError<void> | undefined) => {
if (!success) {
showToast(`连接失败:${error?.code} ${error?.message}`)
return
}
// 连接成功:GATT 连接、服务发现、通知订阅均已完成,可以发送指令
})
- 获取设备信息与Wi-Fi列表(可选)
连接成功后,可获取设备信息(如 productKey、deviceName)和周边 Wi-Fi 列表:
RTCXBluetoothLinkService.getBluetoothDeviceInfo({ sn: device.sn, timeout: 20 },
(success: boolean, response: Object | undefined,
error: BusinessError<void> | undefined) => {
if (!success) {
return
}
const rsp = response as RTCXBluetoothLinkType.IBluetoothLinkResponseData
// rsp.data 为设备返回的原始内容,格式由设备定义(示例设备为 JSON)
})
RTCXBluetoothLinkService.getBluetoothDeviceWifiList({ sn: device.sn, timeout: 20 },
(success: boolean, response: Object | undefined,
error: BusinessError<void> | undefined) => {
if (!success) {
// 设备不支持获取WiFi列表时 error.code 为 0x47,可引导用户手动输入WiFi名称
return
}
const rsp = response as RTCXBluetoothLinkType.IBluetoothLinkResponseData
// 解析 rsp.data 得到WiFi列表
})
rsp.data 为设备返回的原始字符串,字段格式由设备固件定义;部分设备不支持获取 Wi-Fi 列表,此时 success 为 false,error.code 为 0x47。
- 发起绑定并发送配网信息
// 发起云端绑定流程,等待设备配网成功后上线
const addReq: RTCXAddDeviceType.BTAddDeviceReq = {
addDeviceType: RTCXAddDeviceType.EAddDeviceType.BT,
productKey: '设备的productKey', // 可从设备信息获取
deviceName: '设备的deviceName'
}
RTCXAddDeviceService.startAddDevice(addReq)
// 配网内容与二维码配网格式一致
const content = JSON.stringify({
ssid: 'wifi名称',
pwd: 'wifi密码',
type: 0,
random: RTCXAddDeviceService.getBindToken()
})
const netReq: RTCXBluetoothLinkType.IBluetoothLinkNetworkingReq = {
sn: device.sn,
content: content,
timeout: 60 // 等待设备响应的超时时间,单位:秒
}
RTCXBluetoothLinkService.networkingBluetoothDevice(netReq,
(success: boolean, response: Object | undefined,
error: BusinessError<void> | undefined) => {
if (!success) {
showToast(`配网失败:${error?.code} ${error?.message}`)
return
}
// 设备已收到配网信息,等待设备联网上线
})
配网回调成功仅表示设备已收到 Wi-Fi 信息,不代表云端绑定成功。设备联网上线后的绑定结果以 onAddDeviceStatus 回调为准,建议业务层增加整体超时控制(如 120 秒)。
- 配网内容
content需为 JSON 字符串,random通过RTCXAddDeviceService.getBindToken()获取,type固定为 0。 startAddDevice需要在设备联网上线前发起。- 已连接设备时也可使用
connectCombineNetworkingBluetoothDevice一次完成连接和配网,此时sn和content均需传入。
- 监听绑定结果
const addStatusListener: RTCXAddDeviceType.IAddStatusListener = {
onAddDeviceStatus: (status: RTCXAddDeviceType.EAddDeviceStatus,
data: Object | undefined, error: BusinessError<void> | undefined) => {
if (status === RTCXAddDeviceType.EAddDeviceStatus.Success) {
// 绑定成功,data 为新设备信息
} else if (status === RTCXAddDeviceType.EAddDeviceStatus.Failed) {
// 绑定失败,error 为失败原因
}
}
}
- 结束流程
// 停止扫描
RTCXBluetoothLinkService.stopScanBluetoothDevices()
// 取消绑定状态监听
RTCXAddDeviceService.registerStatusListener(undefined)
页面销毁或流程结束时调用,避免扫描和回调残留。
- 蓝牙配网当前未提供主动断开连接的接口。设备收到 Wi-Fi 信息后通常会自动断开蓝牙连接,SDK 会同步释放 GATT 连接资源。
- 扫描启动失败返回
REQUEST_AUTH_CODE_FAIL;设备不存在或连接过程中失败返回CONNECT_FAIL;未找到服务/特征或服务发现失败返回WRITE_UNSUPPORTED;请求超时返回LINK_TIMEOUT。 - 发送指令时若连接未就绪或特征不可写,返回
WRITE_UNSUPPORTED;写入失败返回SEND_MESSAGE_FAIL。 - 设备返回未知或失败指令时,当前所有等待中的请求会以失败回调,
error.code为设备返回的指令值。 - 同一类请求重复调用会覆盖上一次的请求回调,上一次请求不会再收到回调。