安卓微信小程序硬件框架发布

说明

  • 仅支持 32/64 位安卓 ARM 设备。
  • 动态库有更新:此版本有新增和修改 so 文件,若 WMPF 是预装到系统内的,安装后还需要更新和替换系统路径下的 so 文件,否则启动会有 crash 问题。如对替换方式有疑问请联系设备厂商处理。
  • v2.1.0 版本开始,不支持使用 ActivateDeviceByIoT 方式激活的设备。

产物

  • Service Apk 是安卓 WMPF 的服务端,在 WMPF Cli 调用 WMPF 能力之前,需要安装到安卓设备上并保持运行。
    • 文件名带有 alpha 字样是开发版本,带有 WMPF 的测试界面(仅供点击唤醒 WMPF 进程所用,无调试入口)。
    • 文件名带有 production 字样是正式版本,没有操作界面,仅供正式上线后使用(系统把应用保活即可)。
  • 形如 wmpf-cli-*.aar 的文件是 WMPF cli 的 aar 文件,需要下载后集成在开发者自己的应用中,用来调用 WMPF 的能力。

需要帮助

如果在使用过程中遇到任何问题,可以前往「硬件服务」专区查看说明。

如未能解答,请发送邮件至 wx_iot@tencent.com 描述具体问题。

欢迎开发者扫码加入「硬件服务」沟通群一起交流。

设备组

针对某些需要对设备进行批量操作的场景,可以通过设备组完成。

1. 设备组的限制

  • 一个设备组默认最多添加 50 个设备,一个设备只能属于一个设备组。
  • 符合大容量设备组使用条件的,可以使用扩容组。
  • 同一个设备组内的设备必须属于同一设备类型(model_type),例如「校园电话」,但不一定是相同设备型号(model_id)。
  • 设备组必须通过后台 API 创建,创建后不允许修改设备组的名称。用户授权和接听通话时看到的都是设备组的名称,而不能针对用户自定义。

2. 管理设备组

开发者可以通过下列后台接口完成对设备组的增删改查操作。

开发者需要维护创建的所有设备组的 group_id,以便后续查询和授权时使用。微信只做校验,不维护微信小程序下创建的所有 group_id 列表。

  • 创建设备组
  • 从设备组删除设备
  • 向设备组添加设备
  • 查询设备组信息

注意:

除了设备组创建时添加设备外,一般添加或删除设备只应发生在添购设备、设备损坏更换、设备裁撤等场景。请开发者保持设备组内设备的相对稳定,避免过于频繁地修改设备组的组成。

微信会对设备组操作进行监测,并在必要时要求微信小程序管理员进行手动确认。这种情况下,设备组操作会在管理员确认后生效。

3. 适用场景

目前可用于设备批量授权的场景。

4. 问题排查指引

4.1 为什么设备之前添加过设备组了,但是设备组里查不到?

  • 可能由其他逻辑调用了 removeIotGroupDevice 从设备组中将设备删除;
  • 可能由其他地方使用 addIotGroupDevice (force_add=true) 强制将设备转移到了其他设备组,也会导致设备从之前的组里移除。

4.2 用户授权设备组后,仍提示未授权设备(errCode = 9)如何排查?

使用设备组的设备,如果提示未授权,可能有以下几种可能:

  • 用户未授权设备组,或曾经授权后用户清空或取消授权
    • 可以使用授权状态查询接口,判断用户和设备/设备组之间是否存在授权关系
  • 用户授权了设备组,但是设备组内无此设备。
    • 可以使用 getIotGroupInfo 查询设备组中的设备列表。

通话提醒异常排查指南

发起通话成功后,微信后台会使用微信消息通道向用户推送通话提醒。要收到通话提醒,手机端需要满足下列条件:

  • 至少需微信客户端 8.0.30 支持,为保证最佳效果,建议使用 >= 8.0.39 版本。Mac/Windows 微信暂不支持通话提醒;
  • 设备端网络通畅。断网、弱网环境,或受到安卓系统省流、省电策略的限制,会导致通知接收有概率发生延迟或一段时间内无法收到
    • iOS 系统,微信在后台时,推送由苹果统一进行;微信在前台时,推送走微信的消息通道。
    • 安卓系统通知统一走微信的消息通道。某些系统设置(如「智能省流量」、「休眠时始终保持网络连接」、「电池优化」、「省电策略」等)可能影响应用的网络情况(参考第 4 节),使微信消息通道中断,导致无法收到消息或消息延迟。
  • 当前用户已登录手机微信客户端。

通话异常排查指南

在通话过程中,如果碰到「无法发起通话」,「通话发起后异常退出」,「接听方一接听就挂断」,「通话异常退出」等问题,可以参考本文进行排查。

一次通话有 「拨打方」(或「来电方」、「主叫」)「接听方」(或「被叫」) 两个角色。通常,我们可以将一次硬件和微信小程序之间的 VoIP 通话分为 「发起」、「加入」、「等待」和「通话」 四个阶段。不同阶段可能会有不同类型的问题,在排查时,应首先根据表现确定是哪个阶段出现异常,在按照具体阶段的指引进行进一步分析。

建议使用插件 2.3.2 及以上版本。

1. 发起阶段

发起阶段是指调用插件 initByCaller 接口或 Linux SDK wx_voip_session_call 创建 VoIP 房间的阶段。在此阶段中,微信后台会进行一系列通话前置的检查操作,包括但不限于:

  • voipToken 的有效性。
  • 用户和设备之间是否存在授权关系。
  • 微信小程序流量包是否有余量,或设备是否已绑定有效的 license。

校验通话后,微信后台会向接听方推送通话提醒。

在发起阶段如果失败,接口会返回 errCode,开发者可以根据插件文档的说明来排查问题原因,比较常见的错误有以下几类。

(1) 用户未授权设备 (errCode: 9)

设备要和微信用户通话,必须先进行授权,具体过程请参考《用户授权设备》文档

出现此错误,常见的有以下情况:

  • 未使用 wx.requestDeviceVoIP 向用户请求过授权,或请求后用户拒接授权;
  • 用户曾经授权过,但是后续取消了授权;
  • 用户从最近使用中删除了微信小程序。此时会清空该用户和微信小程序间的所有授权记录;
  • 传入的 openId 不是要拨打给用户的,例如:授权的是家长,这里传入了孩子的 openId。

在使用设备组的情况下,常见还有以下情况:

  • 用户授权了设备组 A,但设备未被添加到设备组 A 中或已被 removeIotGroupDevice 接口移除;
  • 用户授权了设备组 A,但设备被使用 addIotGroupDevice (force_add=true) 强制转移到了另一设备组 B,也会导致设备从设备组 A 里移除。

优化建议

建议使用授权状态查询 接口,判断用户和设备/设备组直接是否存在授权关系。

  • 手机微信内发起通话前,建议提前调用wx.getDeviceVoIPList 查询用户已授权设备的列表,判断设备已被授权再发起通话,否则应请求用户重新授权;
  • 设备发起通话前,建议提前调用插件 getIotBindContactList 接口判断设备和用户间是否存在授权关系,存在时再发起通话,否则应提示用户重新授权;

对于设备组,可以使用 getIotGroupInfo 查询设备组中的设备列表。

(2) 设备呼叫手机微信 voipToken 错误 (errCode: 13)

对于使用设备认证 SDK 注册的设备(此时 voipToken 传入 SDK 获取到的 deviceToken),常见有以下情况:

  • deviceToken 过期。deviceToken 是有一定有效期的,需要定时进行更新,如果获取时间过久会失效。
  • 未传入 voipToken 字段或传入空字符串。此方式仅适用于使用 WMPF registerMiniProgramDevice 接口注册的设备。

对于使用 WMPF 注册的设备,可能有以下情况:

  • 设备之前是使用设备认证 SDK 的,未使用 WMPF 的 registerMiniProgramDevice 接口重新注册过。
  • WMPF 低于 1.2.0,或插件版本低于 2.3.0。

2. 「拨打方」加入阶段

创建 VoIP 房间成功后,「拨打方」会直接加入该房间,界面上会显示「连接中…」。加入的时长一般与当前网络状态有关。

加入成功后,拨打方会触发 joinedRoomByCaller 事件。

(1) 设备之前可以拨打成功,突然开始持续失败,需重启 WMPF 才能恢复

大概率是安卓 WMPF 低版本的 bug,请升级到 >= 2.0 版本解决。如新版本仍发现类似问题,请参考第 8 节反馈。

这种情况下开发者可能会收到 joinFailCaller 事件,errMsg 包含 Already in room or joining,此时可以尝试重启 WMPF。

(2) 一直显示「连接中」(卡在本阶段),无法加入房间,通话被突然结束

可能是网络状况较差导致加入过慢,此时通话可能会因接听方等待超时而结束,触发 abortVoipendVoip 事件。

(3) 未加入房间就直接退出通话

常见原因有:

  • 微信小程序错误调用插件 forceHangUpVoip 接口挂断通话,触发 cancelVoip 事件和 endVoip 事件(Toast 提示 「通话已被微信小程序结束」)。
    • 之前排查遇到部分微信小程序会设置定时器来设置通话的最大时长,通话结束后,某些情况下计时器没有清理导致在后续某次通话时随机挂断通话。
    • 我们建议通过监听 calling 事件,并判断 keepTime 来限制通话时长,不建议使用定时器
  • 因网络超时或其他异常导致加入房间失败,这种情况下拨打方会收到 joinFailCaller 事件,可以通过 data 字段拿到 errMsg 和 message 来分析错误原因。

常见问题(FAQ)

通话相关异常,请参考《通话异常排查指南》

1. 功能相关(通用)

1.1 如何限制用户的单次通话时长?

建议使用 initByCallertimeLimit 参数。插件低版本也可以根据 calling 事件的 keepTime 字段计算通话时长。超过限制后可以调用插件 forceHangUpVoip 中断通话。

不建议使用定时器实现此功能,容易出现一些异常情况导致定时器没有被清理的情况。导致影响后续通话。

1.2 在门禁、门锁场景,如何在手机端通话页面实现「开门」等功能?

插件提供了 setCustomBtnText 接口在手机端接听页面自定义按钮,开发者可配置一个自定义的弹层来实现具体功能。

C 端用户体验如下图所示:

1.3 用户如何取消授权?

用户可以在微信小程序设置页里取消授权,或通过在最近使用中删除微信小程序来清空授权记录。请参考「处理授权失效的情况」。

1.4 如何查询用户是否已授权设备(组)?

请参考「授权状态查询」。

1.5 如何设置呼叫超时时长(长时间不接听时停止呼叫)?是否支持轮询呼叫?

开发者可以自行控制超时时间,超时后调用插件 forceHangUpVoip 接口中断通话。

超时后,开发者可以根据业务场景,选择自动拨打给其他用户,实现轮询呼叫的能力。例如 101 号房有 A、B、C 三位业主,打给 A 业主 30 秒未接听,可自动打给 B 业主,依此类推。

1.6 如何自定义手机端看到的设备端来电名称?

为强化设备通话的认知、保证用户体验统一,手机端用户授权设备名称、接听设备来电的名称需保持一致。

授权设备名称 = 来电方名称 = 开发者自定义名称 + 设备类型名称。如「艾玛的希沃网课学习机」。开发者需要考虑名称显示,对名称做好规范。

(案例示意:订阅设备名称、来电方名称、语音通话中设备名称)

1.7 使用物联网卡时,如何配置域名和 IP 白名单?

VoIP 业务依赖于微信基础服务和微信小程序相关的业务内容,涉及比较多的 IP(在几千的量级)和域名,暂时未能提供完整的域名和 IP 列表,目前建议使用非定向的流量。

如有定向流量的需求,可在微信开放社区「硬件服务」板块发帖联系我们。

注意:IP 本身会随着业务的变更而增添或者裁撤,因而暂时没办法提供稳定的列表。

1.8 音视频通话的流量使用情况?

根据测算,语音通话大概是 2MB/分钟,视频是 10-30MB/分钟。

1.9 设备无摄像头或因隐私等原因不希望传画面(门禁、门锁的用户端),如何默认禁用摄像头?

插件发起通话时可以设置 caller.cameraStatus 或 listener.cameraStatus,设置两端是否默认开启摄像头,参见 initByCaller 接口文档。

如果要禁止用户切换摄像头,可以用插件的 setUIConfig 设置 callerUI/listenerUI 的 enableToggleCamera 选项。

1.10 为何推送消息显示的通话时长和通话结束或者 endVoip 事件获取的不一致?应该如何获取准确的通话时长?

VOIP 插件 2.2.1 及以下版本,通话结束页显示的时间为本地定时器计算的时间,endVoip 事件的 keepTime 提供的也是这个时间。但是由于通话双方之间存在一定网络延迟,这里的时间可能与实际扣费时长并不一致(一般要多于实际扣费的时长)。

VOIP 插件 2.2.2 版本开始,会在通话结束后(即 endVoip 事件后)从后台获取实际扣费时长,并通过 finishVoip 事件的 keepTime 返回给开发者。通话结束页也会更新显示实际的扣费时长。

2. 功能相关(安卓设备)

2.1 安卓应用和微信小程序之间如何进行参数传递和通信?安卓应用如何接收微信小程序发来的消息?
  1. 简单的「安卓应用 -> 微信小程序」单向单次传递参数的场景,可以直接在启动微信小程序的 path 中拼接 query。
  2. 如果安卓应用要接收微信小程序发来的事件、需要双向通信或者数据量大时可以通过 WMPF 提供的通信通道(Invoke Channel)
2.2 通话完成后如何关闭微信小程序?

当设备端微信小程序只承载 VOIP 通话能力时,可能需要在通话结束后将微信小程序切后台或关闭。

微信小程序收到插件的 endVoIP 事件后,通过 WMPF 提供的通信通道(Invoke Channel)通知 App。

收到通知后,App 可以选择调用 closeWxaApp 将微信小程序切后台或关闭(可参考《性能与体验优化指南》的说明选择)。

2.3 如何判断当前微信小程序是在设备端(WMPF)还是手机端打开

在 WMPF 运行时,微信小程序能可以访问到 wmpf 这个全局变量。可以通过是否存在这个全局变量来判断:typeof wmpf !== 'undefined' 即为设备端。

注意:调用 wmpf 上的方法前,应提前判断 wmpf 这个全局变量是否存在,否则在手机微信端走到这段逻辑时会报错。

3. 异常相关(通用)

3.1 手机端未收到微信通话强提醒或提醒强度不符合预期(锁屏未提醒、未响铃、未震动等)

请参考《通话提醒异常排查指南》。

3.2 获取设备票据 getSnTicket 接口返回 48001 (api unauthorized)

微信小程序 appId 未完成硬件设备接入导致。请确认:

  • appId 对应微信小程序已在「微信小程序管理后台」完成硬件设备接入。
  • 请求时使用的 access_token 是通过完成申请的微信小程序的 appId 申请的,而不是其他微信小程序的 appId 或者移动应用的 hostAppId。
3.3 wx.requestDeviceVoIP 报错 invalid scope

微信小程序 appId 未完成硬件设备接入或接入后未申请「微信小程序音视频能力」设备能力导致。请确认微信小程序已在「微信小程序管理后台」完成硬件设备接入并申请通过微信小程序音视频能力。

3.4 wx.getEnterOptionsSync 或插件的 getPluginEnteroptions 无法获取到进入微信小程序的 query

一般有以下几种情况:

  • 这两个函数只能获得微信小程序启动时(冷启动或热启动)的参数,如果是通过 wx.navigateTo 等路由方式跳转页面的情况,则需要在对应页面的 onLoad 生命周期获取。
  • 由于插件和宿主微信小程序的安全策略限制,当微信小程序启动路径为插件页面时,需要通过 VOIP 插件提供的 getPluginEnteroptions 获取 query;当微信小程序启动路径为微信小程序页面时,需要通过 wx.getEnterOptionsSync 获取 query。

建议排查时同时打印返回值中的 path 字段,确认是否是预期的传入 querypath

3.5 接听方接听时提示「页面不存在」

一般有以下几种情况:

  • 调用插件 initByCaller 时未设置 miniprogramState,或设置了 miniprogramState: formal,此时接听方会打开正式版微信小程序。而设备 VOIP 能力尚未发布正式版。
  • 调用插件 initByCaller 时设置了 miniprogramState: trial,此时接听方会打开体验版微信小程序。而当前设置为体验版的微信小程序中并未支持设备 VOIP 能力。
  • 调用插件 initByCaller 时设置了 miniprogramState: developer,此时接听方会打开开发版微信小程序。此时接听方需要提前扫码下载与拨打方相同的开发版微信小程序方可使用。
3.6 发起通话后,插件页面一直停留在「等待进行通话」界面无反应

一般有以下几种情况:

  • 微信小程序未调用插件 initByCaller 发起通话。可能是前置逻辑异常或未走到发起通话的分支。开发者应首先确定调用了该接口。
  • 微信小程序调用插件 initByCaller 失败,可能会抛出异常或者返回了非 0 的 errCode。开发者应正确地捕获和处理接口异常,并给用户必要的提示。
3.7 为什么我在 wecopper 的设备管理里找不到公钥?

这是因为你的设备类型是微信支付刷脸设备,目前这类设备不支持硬件 Voip 模式,需要重新申请设备类型。

4. 异常相关(安卓设备)

4.1 WMPF 获取不到正确的摄像头,或摄像头画面旋转

可以使用 InitGlobalConfig 接口指定微信小程序使用的摄像头,也可以指定摄像头画面的旋转角度。

fun initGlobalConfig() {
    val jsonConfig = JSONObject()
        // 请注意:USB 摄像头和内置摄像头使用的参数名称是不一样的。
    try {
        // 案例 1:微信端画面颠倒
        jsonConfig.put("cameraPushFlip", true) // USB 摄像头需使用 usbCameraPushFlip 参数

        // 案例 2:使用内置摄像头,微信端显示画面旋转
        jsonConfig.put("cameraRotationAngle", 90) // 根据实际情况调整角度

        // 案例 3:通过指定 internalCameraName 使用设备内置摄像头(需 WMPF 2.0.0 支持)
        jsonConfig.put("internalCameraName", "xxxx")

        // 案例 4:通过指定 cameraId 使用设备内置摄像头
        jsonConfig.put("cameraId", 0)

        // 案例 5:通过直接指定摄像头设备路径使用 USB 摄像头(与案例 5 的情况二选一)
        jsonConfig.put("usbCameraName", "/dev/xx/xx/xx")

        // 案例 6:通过指定三元组使用 USB 摄像头(与案例 4 的情况二选一)
        jsonConfig.put("usbCameraProductId", 0)
        jsonConfig.put("usbCameraVendorId", 0)
        jsonConfig.put("usbSerialNumber", "xxx")

        // 案例 7:使用 USB 摄像头,WMPF 预览和微信端显示画面旋转
        jsonConfig.put("usbCameraRotationAngle", 90) // 根据实际情况调整角度

        val json = jsonConfig.toString()
        LogUtils.d(TAG, "initGlobalConfig", json)
        Api.initGlobalConfig(json)
            .subscribe({
                LogUtils.d(TAG, GsonUtils.toJson(it))
                warmLaunch()
            }, {
                LogUtils.d(TAG, GsonUtils.toJson(it))
                warmLaunch()
            })
    } catch (e: Exception) { }
}

如果设置摄像头画面旋转未生效,建议按照下列指引检查:

  • InitGlobalConfig 必须在 ActivateDevice 回调成功后、启动微信小程序前调用。建议在 ActivateDevice 的 onSuccess 回调后调用。
  • InitGlobalConfig 设置是一次性的,在每次 WMPF 启动后都需要调用。
  • 请确认设备使用的是 USB 摄像头还是内置摄像头
    • 如果通过 usbCameraName,或 usbCameraProductId + usbCameraVendorId + usbSerialNumber 指定使用 USB 摄像头,需使用 usbCameraPushFlipusbCameraRotationAngle 设置画面旋转
    • 其他情况下使用内置摄像头,可以使用 internalCameraName 指定摄像头 cameraId。此时需使用 cameraPushFlipcameraRotationAngle 设置画面旋转。此时只能设置微信客户端看到的推流画面的旋转,不能改变设备端看到的预览画面。

性能与体验优化

要让用户的接听和拨打体验更加流畅,关键是要缩短接听和拨打时微信小程序启动和一些网络请求的耗时。

微信小程序的冷启动需要一定的时间,尤其是在性能较差的设备上,启动耗时可能会偏长。影响用户拨打和接听音视频通话的体验。

1. 微信小程序侧启动性能优化

建议开发者参考《启动性能优化文档》优化微信小程序的启动耗时。

2. 安卓 WMPF 微信小程序预热(建议)

在设备端 WMPF,我们额外提供了「微信小程序预热」的能力,在用户使用微信小程序前,就预先将微信小程序在后台以无界面的形式启动并常驻运行,以便用户使用时可以直接把微信小程序切前台,而不需要完整进行冷启动流程。流程如下:

  • WMPF 激活后,在用户使用微信小程序之前,可以调用warmUpApp提前预热微信小程序。
    • 通常情况下,建议指定 path 为插件的拨打/接听页面 plugin-private://wxf830863afde621eb/pages/call-page-plugin/call-page-plugin?isPreLaunch=1。如果开发者需要微信小程序启动时打开其他页面(例如联系人列表页),也可以指定预热其他页面。
  • 设备端发起或接听通话,真正需要使用微信小程序时,再调用launchMiniProgram传入正常的带有 query 的 path 等启动参数,即可复用之前预热的环境,把微信小程序拉到前台。
    • 预热和正式使用时传入的 path 参数的路径部分需保持一致,query 部分可不同。否则会额外触发一次页面的 reLaunch

手机微信呼叫设备(Linux 直连)

需插件 2.4.0 版本、Linux SDK 0x00097 开始支持

如果要获取通话过程的各类事件,可以使用插件的 onVoipEvent 接口。

1. 手机微信端发起通话

发起通话前,一般需要用户在微信小程序中选择拨打的设备和通话的类型(音频或视频)。

发起通话时,开发者需要在微信小程序中调用插件的 callDevice 接口获取 roomId,然后跳转到插件的发起通话页面。

const wmpfVoip = requirePlugin('wmpf-voip').default

try {
  const { roomId } = await wmpfVoip.callDevice({
    roomType: 'video', // 房间类型。voice: 音频房间;video: 视频房间
    sn: '设备 SN',
    modelId: '设备 modelId',
    nickName: '设备端显示的微信用户名称',
    deviceName: '我的学习机',
  })

  if (/* 当前不在插件页面 */) {
    wx.redirectTo({
      url: wmpfVoip.CALL_PAGE_PATH,
    })
  }
} catch (e) {
  console.error('callDevice failed:', e)
  wx.showToast({
    title: '呼叫失败',
    icon: 'error',
  })
}

2. 推送通话提醒

手机微信内发起通话后,开发者应使用自有消息通道,将 roomId 等设备端加入房间所需参数传递给设备。

3. 设备端接听通话

设备端在收到通话提醒后,首先使用 wx_voip_session_new 接口创建 Session,然后通过 wx_voip_listener_join 接听。其他接口使用与设备呼叫手机类似。Session 创建后,可以调用 wx_voip_session_hangup 接口结束通话。详情可参考《微信小程序音视频通话 SDK (Linux)》。

4. 设备端拒绝通话

设备端收到通话提醒后,可以调用 wx_voip_listener_hangup(只能在 Session 创建前调用)结束通话,实现忙线或拒接。详情可参考《微信小程序音视频通话 SDK (Linux)》。

手机微信呼叫设备(安卓)

用户可以向设备发起音视频通话,设备端需要开发者在收到消息后拉起微信小程序的指定页面让用户接听通话。

如果要获取通话过程的各类事件,可以使用插件的 onVoipEvent 接口。

1. 手机微信端发起通话

发起通话前,一般需要用户在微信小程序中选择拨打的设备和通话的类型(音频/视频)。

发起通话时,开发者需要先从后台拿到从设备端获取的 pushToken,并在微信小程序中调用插件的 callWMPF 接口,然后跳转到插件的发起通话页面。

const wmpfVoip = requirePlugin('wmpf-voip').default

const roomType = 'video'
try {
  const { roomId, isSuccess } = await wmpfVoip.callWMPF({
    roomType: 'video', // 房间类型。voice: 音频房间;video: 视频房间
    sn: '设备 SN',
    modelId: '设备 modelId',
    pushToken: '从设备获取的 pushToken',
    nickName: '设备端显示的微信用户名称',
    deviceName: '我的学习机',
    envVersion: 'release', // 指定接听方使用的微信小程序版本,开发过程可以使用 develop
  })

  if (/* 当前不在插件页面 */) {
    // 跳转到插件的通话页面
    wx.redirectTo({
      url: wmpfVoip.CALL_PAGE_PATH,
      // 插件 2.3.9 开始支持 CALL_PAGE_PATH, 低版本请传入 'plugin-private://wxf830863afde621eb/pages/call-page-plugin/call-page-plugin',
    })
  }
} catch (e) {
  console.error('callWMPF failed:', e)
  // 参数错误的情况会通过异常抛出
  wx.showToast({
    title: '呼叫失败',
    icon: 'error',
  })
}

注意

  • 建议开发者在服务端维护 sn 与 pushToken 的关联,提前在设备端获取 pushToken并存到后台,并在 pushToken 过期前进行刷新。
  • 发起通话时,需要保证设备已激活并联网在线
  • 给设备推送的消息会在调用发起通话接口后由微信后台直接下发,不需要开发者额外调用服务端下发消息的接口。
  • 插件 2.4.0 以下版本,需使用initByCaller接口呼叫设备,传入 businessType: 2。使用 initByCaller 接口发起的通话,需要用户额外进行授权。

2. 设备端接听通话(安卓)

在手机微信端发起通话后,设备端会收到一条 WMPF 的推送消息。需要开发者处理通知并拉起微信小程序展示通话界面:

2.1 绑定消息监听

开发者需要在 WMPF 中调用 registerPushMsgEventListener 注册消息监听。注意,这一步必须在通话发起前进行。

2.2 展示来电通知/提醒(可选)

收到消息后,开发者可以根据产品需要展示来电通知(样式可以自定义),也可直接拉起微信小程序让用户进行接听。

2.3 打开微信小程序接听

推送消息为 JSON 字符串,解析后格式如下

{
  "path": "plugin-private://wxf830863afde621eb/pages/call-page-plugin/call-page-plugin?roomType=roomType&groupId=groupId&listenerId=设备sn&callerName=拨打方名称&customQuery字符串", // 微信小程序启动路径
  "appType": 0, // 0: 正式版 1: 开发版 2: 体验版
  "appid": "wx********" // <a href="https://weixin-xiaochengxu-kaifa.yuannext.com">微信小程序</a>appid
}

开发者需要使用上述参数,调用 WMPF launchMiniProgram 接口打开微信小程序的接听界面。

注意

  • 如果开发者在收到消息后展示了自定义的来电通知,可以在启动微信小程序的 path 后添加 &isClickedHangOnBtn=1。此时用户进入微信小程序就会直接接听通话,不需要再次点击插件通话页面「接听」按钮。
  • 建议在安卓 APP 中使用 WMPFLifeCycleManager 监听 WMPF 退出事件,并重新启动并激活 WMPF,以防止 WMPF 异常退出后消息丢失。
  • 为加快接听通话的速度,建议在收到消息监听时调用prefetchDeviceToken 预拉取设备凭证。
  • 设备上,APP 拉起微信小程序或接听通话较慢时,请参考性能与体验优化指南。

3. 设备端处理通话结束

设备端通话结束后,开发者需自行处理页面跳转或关闭微信小程序。一般有以下几种方式:

  • 结束后跳转其他页面:开发者需要通过插件 setVoipEndPagePath 接口设置通话结束跳转的页面。开发者未设置时则停留在通话记录页面。
  • 结束后微信小程序切后台:开发者可以监听插件 endVoip 或 finishVoip 事件,通过 WMPF 提供的通信通道(Invoke Channel)通知移动应用,使用closeWxaApp (keepRunning=true) 将微信小程序切后台。
  • 结束后关闭微信小程序:开发者可以监听插件 endVoip 或 finishVoip 事件,调用 wx.exitMiniProgram 关闭微信小程序。

设备呼叫手机微信

在用户对设备进行授权后,设备可以向已授权用户发起音视频通话,用户在微信内打开微信小程序进行接听。

硬件开发者需建立微信小程序用户 openId、微信小程序 appId、硬件设备之间的关联。用户在手机端授权后设备才可拨打。

如果要获取通话过程的各类事件,可以使用插件的 onVoipEvent 接口。

1. 设备端发起通话(安卓直连)

发起通话前,一般需要用户选择拨打给的用户和通话的类型(音频/视频)。

根据业务场景不同,发起通话前的流程(如选择联系人和房间类型)可以在微信小程序的另一个页面中或者安卓应用中进行。

1.1 微信小程序页面进入通话页面

适用于用户发起通话前的页面(如联系人选择等)是微信小程序页面时。

发起通话时,设备端需要在之前的页面中调用插件的 initByCaller 接口,然后跳转到插件的发起通话页面。

const wmpfVoip = requirePlugin('wmpf-voip').default

try {
  // 2.4.0 以下版本 roomId 为 groupId
  const { roomId, isSuccess } = await wmpfVoip.initByCaller({
    caller: {
      id: 'sn', // 设备 SN
      // 不支持传 name,显示的是授权时「deviceName」+「modelId 对应设备型号」
    },
    listener: {
      // 参见 https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html 获取
      id: 'openId' // 接听方 用户 openId
      name: 'xxxxxx', // 接听方名字,仅显示用
    },
    roomType: 'video', // 房间类型。voice: 音频房间;video: 视频房间
    businessType: 1, // 1 为设备呼叫手机微信
    voipToken: 'xxxxxxxxxx', // 使用设备认证 SDK 注册的设备传入 deviceToken,使用 WMPF RegisterMiniProgramDevice 接口注册的设备无需传入(插件 2.3.0 支持)
    miniprogramState: 'formal', // 指定接听方使用的微信小程序版本
  })

  if (isSuccess) {
    // 如果微信小程序启动直接进入插件页面,则不要调用 wx.redirectTo
    wx.redirectTo({
      url: wmpfVoip.CALL_PAGE_PATH,
      // 插件 2.3.9 开始支持 CALL_PAGE_PATH, 低版本请传入 'plugin-private://wxf830863afde621eb/pages/call-page-plugin/call-page-plugin',
    })
  } else {
    wx.showToast({
      title: '呼叫失败',
      icon: 'error',
    })
  }
} catch (e) {
  // 参数错误的情况会通过异常抛出
  wx.showToast({
    title: '呼叫失败',
    icon: 'error',
  })
}

1.2 安卓应用直接进入通话页面

适用于用户发起通话前的页面(如联系人选择等)是安卓应用页面时。

发起通话时,需要安卓应用调用 WMPF launchMiniProgram 接口拉起微信小程序,path 直接使用插件的拨打页面 plugin-private://wxf830863afde621eb/pages/call-page-plugin/call-page-plugin。路径后可以带自定义参数,如 &a=1

这种情况下,开发者可以直接在微信小程序 App.onShow 时调用 initByCaller,插件会直接进入拨打状态,不需要也不可以再跳转到插件页面

强烈建议开发者在启动参数中增加防重放参数,例如callSeq=1703741306977。参数名可以自定义,取值可以是时间戳或其他唯一 ID。主要起到以下功能

  • 如果用户点击重新进入微信小程序,避免重复发起通话。
  • 微信小程序切后台又切前台的情况(如进入微信小程序设置页、进入其他原生页面、用户手动操作等),App.onShowcallPageOnShow 会多次触发,避免重复发起通话。
  • 标识当前启动是会发起通话,方便一些逻辑判断。

建议开发者使用「微信小程序预热」能力加快微信小程序的启动速度。

const wmpfVoip = requirePlugin('wmpf-voip').default

// 假设预热启动参数为 ?isPreLaunch=1
// 假设发起呼叫时启动参数为 ?callSeq=1703741306977&..其他呼叫用的参数

function checkCallSeq(seq) {
  if (!seq) return false
  // 重新进入微信小程序会重启微信小程序,因此 seq 需要持久化存储,不能仅存变量
  const lastCallSeq = wx.getStorageSync('WMPF_CALL_SEQ')
  if (seq !== lastCallSeq) {
    // 示例仅做简单的比较,开发者可以根据业务需要增加其他判断条件,如 parstInt(seq) > parseInt(lastCallSeq)
    wx.setStorageSync('WMPF_CALL_SEQ', seq)
    return true
  } else {
    // 重复发起的通话
    return false
  }
}

function call(options) {
  try {
    wmpfVoip
      .initByCaller({
        /* 参数可从 options 中获取,此处省略 */
      })
      .catch(e => {
        wx.showToast({
          title: '呼叫失败',
          icon: 'error',
        })
      })
  } catch (e) {
    // 参数错误的情况会通过异常抛出
    wx.showToast({
      title: '呼叫失败',
      icon: 'error',
    })
  }
}

/**
 * 调用 WMPF LaunchMiniProgram 时,
 *  - 如果微信小程序不在前台(未启动或在后台),会触发 App.onShow
 *  - 如果微信小程序在前台,App.onShow 不会触发,但会触发插件的 callPageOnShow
 */

App({
  onShow() {
    const { query } = wmpfVoip.getPluginEnterOptions()
    if (query.isPreLaunch) {
      // 微信小程序预热场景,无需处理
      return
    }

    if (!query.callSeq) {
      // 当前启动不是发起通话,无需处理
      return
    }

    if(!checkCallSeq(query.callSeq)) {
      // 重复发起通话,直接忽略
      return
    }

    call(query)
  },
})

wmpfVoip.onVoipEvent(event => {
  if (event.eventName === 'callPageOnShow') {
    // 仅处理微信小程序在前台时,调用 WMPF LaunchMiniProgram 触发<a href="https://weixin-xiaochengxu-kaifa.yuannext.com">微信小程序</a> reLaunch 的情况
    const query = wmpfVoip.getPluginOnloadOptions()
    if (checkCallSeq(query.callSeq)) { 
      // 此处可以过滤掉已被 App.onShow 处理的情况
      call(query)
    }
  }
})

注意

  • 给用户推送的接听提醒会在调用 initByCaller 后由微信后台直接下发,不需要开发者额外调用服务端下发消息的接口。
  • roomType 等参数可以通过拉起微信小程序的 path 中的 query 传递给微信小程序。
  • 设备上,APP 拉起微信小程序或接听通话较慢时,请参考性能与体验优化指南。

2. 设备端发起通话(Linux 直连)

请参考 《微信小程序音视频通话 SDK (Linux)》 5.4.1 发起通话部分

3. 手机微信端接听通话

用户在手机端可以收到「响铃+振动」的强提醒通知,点击接听按钮后,会启动微信小程序并直接进入「VOIP 通话」插件页面接听通话。

完成通话后,微信客户端内会显示本次通话的信息与「关闭」按钮,用户点击「关闭」按钮后再跳转开发者调用setVoipEndPagePath设置的页面。开发者未设置时则直接关闭微信小程序。

开发者可以自定义接听页面按钮,以及通话结束跳转页。详情请参考插件文档

4. 设备端处理通话结束(安卓直连)

设备端通话结束后,开发者需自行处理页面跳转或关闭微信小程序。一般有以下几种方式:

  • 结束后跳转其他页面:开发者需要通过插件setVoipEndPagePath接口设置通话结束跳转的页面。开发者未设置时则停留在通话记录页面。
  • 结束后微信小程序切后台:开发者可以监听插件 endVoip 或 finishVoip 事件,通过 WMPF 提供的通信通道(Invoke Channel)通知移动应用,使用closeWxaApp (keepRunning=true) 将微信小程序切后台。
  • 结束后关闭微信小程序:开发者可以监听插件 endVoip 或 finishVoip 事件,调用 wx.exitMiniProgram 关闭微信小程序