让微信能给你设备发送文件消息(二维码快速绑定方式)-微信硬件平台开发(2)

avatar
java @CVTE

前言

目前微信使用人数特别多,假如你想通过微信给你的设备推送消息,可以对接一下微信硬件平台。
微信用户绑定设备后,可以通过微信给自己设备发送文件(充当文件助手)或者链接(充当分享好文快捷入口)等功能;

一、对接概览

对接效果:

本质: 微信和我们设备的sn进行绑定,然后下推一下类似IOT指令,三方服务商接收这个类似IOT指令,然后在设备上完成相应的功能。

核心的两个问题:
1、文件会发给谁?

1、微信用户绑定设备后将会有关系:微信用户唯一id(openid) - 三方厂商设备唯一id(sn)
2、厂商用户使用设备会有绑定关系:三方厂商设备唯一id(sn)- 用户唯一id
有了上面两个绑定关系,我们就能通过设备的sn进行转换,实现微信用户到我们厂商用户的映射

2、文件是怎么发的?

image.png

文件是微信先暂存了一次,然后通过HTTP回调的方式通知三方业务,三方业务做相应的消息存储和展示。

职责:

  • 微信:提供微信用户和三方设备绑定推送文件消息的功能
  • 厂商:完成用户设备绑定对接,并对接收到的消息进行处理

二、对接前准备

image.png
微信硬件平台上配置的参数:

image.png

名词解析:

  • appId、secret:小程序注册后能获取到这个的,用途:
    • 需要通过这两个值来获取调用接口的凭证;
    • 需要通过appid跟微信硬件平台绑定,绑定后,才能通过**微信**扫码打开绑定页
  • 回调地址:在微信硬件平台上面配置,通过微信给设备推消息的时候,微信会将信息通过回调调用这个回调地址(包括绑定、解绑、消息推送等);
  • token:微信硬件平台自动生成的,通过他跟回调的参数进行拼接,对数据的完整性进行校验,判断这个调用是否来自于微信;
  • procutID:需要在微信硬件平台上面添加,用于设备绑定。

image.png

三、微信硬件平台

名词解释:

  • product_id: 微信硬件平台对具体某个厂商制造的某款设备产品的标识
  • SN: 厂商后台为设备生成的可对外的全局唯一设备标识, product_id范围内唯一
  • ilink_im_sdk_id: 设备在微信硬件平台的唯一标识,即微信硬件ID
  • ilink_device_ticket: 设备绑定临时身份凭证,五分钟有效期
  • ilink_iot_user_id: 设备绑定者在微信硬件平台的身份标识

image.png 限制:

  • 一个设备只能跟一个用户绑定,即:一个用户可以拥有多个设备,但是一个设备不能属于多个用户
  • 文档中有限制文件在30M之内,但是试了一下,超过30M也是可以上传。

四、对接方案

时序图如下:

image.png


原理就到这里,后面是一些接口的调用,不感兴趣可以不看

1、获取access_token

接口说明:

通过微信开放平台调用微信内部接口时,需要使用appid与secret换取调用凭证access_token

请求地址:

GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=xxx&secret=xxx

请求参数:

属性类型必填说明
grant_typestring填写 client_credential
appidstring小程序/APP唯一凭证,即 AppID,可在「微信公众平台 - 设置 - 开发 设置」页中获得。(需要已经成为开发者,且帐号没有异常状态)
secretstring小程序/APP唯一凭证密钥,即 AppSecret,获取方式同 appid

返回值:

属性名类型说明
access_tokenstring获取到的凭证
expires_innumber凭证有效时间,单位:秒。目前是7200秒之内的值。
errcodenumber错误码, 0为成功
errmsgstring错误信息

2、设备SN注册

接口说明:

每一个设备绑定前,需要到微信硬件平台注册信息

请求地址:

POST https://api.weixin.qq.com/ilink/api/cloud_register_device?access_token=xxx
url上请求参数:

属性类型必填说明
access_tokenstring获取到的凭证,来自上面接口获取access_token接口

请求参数:

{
    "product_id": 上面申请的procut_id,
    "iot_device_list": [
        {
            "sn": "48210927315672"  //设备sn
        },
        {
            "sn": "48210927315673" // 设备sn
        }
    ]
}

属性类型必填说明
product_idnumber设备产品唯一标识
snstring厂商分配的设备唯一序列号
  • 单次接口调用,sn个数上线是100,超限将返回错误
  • 产品上线前, 只能注册20个sn

返回值:

{
    "iot_device_list": [{
        "sn": "48210927315672",
        "ilink_im_sdk_id": "AAYAABPamWLEhIPAAdkg-oTucvVKrSVfJAxxxB5Uw@ilink.im.sdk"  // 这个就是在微信上注册后的硬件id
    }, {
        "sn": "48210927315673",
        "ilink_im_sdk_id": "AAYAABPamWI1HBAQAdkg-pDbVJ0xLQXTuxxxyUAwk@ilink.im.sdk"
    }],
    "errcode": 0,
    "errmsg": "",
    "err_device_list": []
}

3、获取二维码接口

接口说明:
厂商后台到微信硬件平台拉取设备的绑定二维码,用户可使用微信扫描二维码绑定该设备。
请求地址:
POST https://api.weixin.qq.com/ilink/api/mmiot/get_device_qrcode?access_token=xxx
url上请求参数:

属性类型必填说明
access_tokenstring获取到的凭证,来自上面接口获取access_token接口

body请求参数:

{
"ilink_im_sdk_id": "xxx@ilink.im.sdk"
}
属性类型必填说明
ilink_im_sdk_idstring微信硬件平台分配的设备唯一标识,来自上面接口设备SN注册接口,传递这个值,扫完这个码,就是打开这个设备的绑定页(小程序)

返回值:

{
    "errcode": 0,
    "errmsg": "xxx",
    "payload": {
        "device_qrcode_url": "https://mmae.qpic.cn/xxxxxxxx",
        "expire_seconds": 300
    }
}
属性类型说明
errcodenumber返回码,0为成功
errmsgstring错误信息
device_qrcode_urlstring二维码图片链接
expire_secondsnumber二维码失效时间,以秒为单位

下载打开这个device_qrcode_url是一个二维码,打开这个二维码,会打开我们绑定上面绑定的二维码,界面如下:
可绑定,点击绑定即可完成绑定:


设备已经被绑定:

5、回调接口

回调的地址:

POST http://$callback_url?signature=xxx&timestamp=xxx&nonce=xxx

  • callback_url为我们在微信硬件平台配置的接口
  • 回调后我们需要对签名进行校验,判断来源的合法性,这里需要上面在硬件平台配置的签名材料token

签名规则:

  • 将token、timestamp、nonce三个参数进行字典序排序
  • 将三个参数字符串拼接成一个字符串进行sha1加密
  • 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

例子:

token = 8GhcGcYyz70012
nonce=1410310936
timestamp=1636537701

signature = sha1(141031093616365377018GhcGcYyz70012)  = 9d8ed9a3e985d2255807680ce8d450bd06fbde14

调用场景:

主要包括以下几个内容

  • 设备绑定:  topic : /ilink/sys/wechat_iot/productid/product_id/ilink_im_sdk_id/bind
  • 设备解绑:topic:/ilink/sys/wechat_iot/productid/product_id/ilink_im_sdk_id/unbind
  • (标准物联模型)设备发送消息: topic:/ilink/sys/wechat_iot/ productid/product_id/ilink_im_sdk_id/invoke_device_service

主要通过topic字段去区分目前是哪种类型的接口调用,然后将接口中的product_id,和ilink_im_sdk_id截取出来,当成我们需要的参数,用于后续的匹配

  • 设备绑定:通过product_id和ilink_im_sdk_id 去完成相应设备的绑定
  • 设备解绑:通过product_id和ilink_im_sdk_id 去解除相应设备的绑定
  • 设备发送消息:将设备的消息保存,下载响应文件,便于后续的查询

5.1 微信绑定回调

接口说明:

当小程序管理端绑定设备时,微信硬件平台回调厂商后台进行绑定. 厂商可以在绑定回调时进行自己的绑定管理和权限校验.

请注意该接口的可用性, 如果回调失败, 绑定流程也将失败

请求参数:

{
    "topic": "/ilink/sys/wechat_iot/$product_id/$ilink_im_sdk_id/bind",
    "payload": {
        "binder_type": 1,
        "binder_info":{
            "ilink_iot_user_id": "xxx"
        },
        "device_info":{
            "ilink_im_sdk_id": "xxx"
        },
        "ilink_device_ticket": "xxx"
    }
}
属性类型必填说明
binder_typeint绑定者类型. 0=普通绑定者, 1=管理员
ilink_iot_user_idstring微信硬件平台分配的绑定者身份标识,这里就是随机生成的一个用户id
ilink_im_sdk_idstring微信硬件平台分配的设备唯一标识
ilink_device_ticketstring绑定ticket, 厂商在快速对接场景下可以通过该ticket关联微信用户与自有用户. 其他绑定场景不一定有该字段, 请勿使用

返回值:


{
    "errcode":0,
    "errmsg":"xxx",
}

5.2 解绑回调

接口说明:

当用户在微信硬件页解绑设备(见下图,无需开发)或小程序管理端解绑设备(这个是需要自己开发页面的)时,厂商可以在解绑回调时进行自己的绑定管理和权限校验.

请注意该接口的可用性, 如果回调失败, 解绑流程也将失败.

1、微信硬件也解绑设备(无需开发
打开路径:我的 -> 设备 -> 设备 -> 在要解绑的设备右滑 -> 删除

2、小程序管理端解绑设备(需要开发

如何跳转到解绑设备页

页面路径:/pages/delete-devices/delete-devices

传递参数:{"sdkIdList": ["xxx1", "xxx2", "xxx3"]}, 要解绑的设备sdkId列表(前面的ilink_im_sdk_id,微信硬件平台的设备唯一标示)

回跳参数也放在extraData里:{from: 'wx-iot', "successList": ["xxx1", "xxx2"], "failList": ["xxx3"]}, 里面放的是sdkid列表,表明哪些成功,哪些失败

5.3 消息推送回调

绑定完设备后即可获得长按微信消息->打开->发送到设备的能力

注意:目前仅支持30M以下文件的发送,虽然超过30M也成功了

下面是微信发送消息后,推送的对象

class WxStdSendMsg{
    WxStdSendFileResp WxStdSendFile( WxStdSendFileReq req );
    WxStdSendPoiResp WxStdSendPoi( WxStdSendPoiReq req );
    WxStdSendMusicResp WxStdSendMusic (WxStdSendMusicReq req);
    WxStdSendUrlResp WxStdSendUrl (WxStdSendUrlReq req);
}
 
class WxStdSendFileReq{
    @Spec(name="文件类型")
    String type;    //如"pdf","docx"
 
    @Spec(name="文件名")
    String name;    //如"文件.pdf"
 
    @Spec(name="下载链接")
    String download_url;    //下载的内容经过加密, 需要厂商解密
     
    @Spec(name="加密算法")
    String encrypt_algo;    //目前仅支持AEAD_AES_256_GCM
     
    @Spec(name="秘钥base64")
    String key_base64;
     
    @Spec(name="iv base64")
    String iv_base64;   //解密的初始向量
     
    @Spec(name="tag base64")
    String tag_base64;
}
 
class WxStdSendFileResp{
}
 
 
class WxStdSendPoiReq{
    @Spec(name="纬度")
    double latitude ;
 
    @Spec(name="经度")
    double longitude ;
 
    double  scale;
 
    @Spec(name="地点标签")
    String label;// xx省xx市xx区xx路xx号
 
    @Spec(name="地点名称")
    String name;// xxx动物园
}
 
class WxStdSendPoiResp{
}
 
class WxStdSendMusicReq {
    @Spec(name="标题")
    String title;       // 微信内的音乐卡片标题,一般是歌曲名
 
    @Spec(name="描述")
    String description;    // 微信内的音乐卡片描述,一般是歌手名
 
    @Spec(name="网页url")
    String url;            // 音频网页的URL地址
     
}
 
class WxStdSendMusicResp{
}
 
 
class WxStdSendUrlReq {
    @Spec(name="标题")
    String title;      // 微信内的链接卡片标题
 
    @Spec(name="描述")
    String description;    // 微信内的链接卡片描述
 
    @Spec(name="链接")
    String url;
}
 
class WxStdSendUrlResp {
}
                           

卡片链接和音乐,将会是一个链接的提供,理论上不会过期;

如果是文件,如txt、pdf、xlxs 则会返回文件下载的链接,官网说是1天后过期。

出于安全性考虑, download_url下载的内容经过了加密, 算法为AEAD_AES_256_GCM. 厂家需要通过请求中的key_base64,iv,tag_base64等作为参数, 将下载内容解密得到明文. 目前大部分编程语言都支持了AEAD_AES_256_GCM算法 ,解密见:解密文件,AEAD_AES_256_GCM

样例:

发送文件:

{
    "payload": {
        "ilink_im_sdk_id": "AAYAABxxxaAdkg-hgrHdnGvV_nnYrHpl3ux4s@ilink.im.sdk",
        "service_identifier": "WxStdSendMsg.WxStdSendFile",
        "params": {
            "type": "xlsx",
            "download_url": "https://mmae.qpic.cn/204/20303/stodownload?filekey=30340201010420301e020200cc040253480410183c43debb38c2199ce6404583673bac0202200c040d00000004627466730000000131&hy=SH&storeid=32303232303432313134343432343030306630303366353063663362343765653336623030623030303030306363&bizid=1023",
            "iv_base64": "9Z0x8oIcjzOG2Z7w",
            "encrypt_algo": "AEAD_AES_256_GCM",
            "name": "2222222.xlsx",
            "tag_base64": "mGuamsbaxv2ajkcMqFa4cw==",
            "key_base64": "yt8b42s6rTknMWs1KmvSB4cgY//TnX3SX8Z8NdhGIRM="
        },
        "ilink_trace_id": "91675CB035B0B684B237B67423103B44"
    },
    "topic": "/ilink/sys/wechat_iot/3683/AAYAABxxxaAdkg-hgrHdnGvV_nnYrHpl3ux4s@ilink.im.sdk/invoke_device_service"
}

发送QQ音乐:

{
    "payload": {
        "ilink_im_sdk_id": "AAYAABPxxxxKRji8aAdkg-hgrHdnGvV_nnYrHpl3ux4s@ilink.im.sdk",
        "service_identifier": "WxStdSendMsg.WxStdSendUrl",
        "params": {
            "url": "https://i.y.qq.com/v8/playsong.html?hosteuin=oKnz7ioqoKnzNn**&sharefrom=&from_id=0&from_idtype=0&from_name=&songid=9079096&songmid=&type=0&platform=(10rpl)&appsongtype=(11rpl)&_wv=1&source=qq&appshare=iphone&media_mid=003IIWWC1oJ239&ADTAG=wxfshare",
            "description": "张卫健",
            "title": "把酒狂歌"
        },
        "ilink_trace_id": "1C69F0475F0377FF01D1DF919A6049DE"
    },
    "topic": "/ilink/sys/wechat_iot/3683/AAYAABPxxxxKRji8aAdkg-hgrHdnGvV_nnYrHpl3ux4s@ilink.im.sdk/invoke_device_service"
}

标准物模型参数格式

一个标准物模型的属性,服务或事件名, 有可能和自定义物模型或其他标准物模型重复. 因此在物模管理中的设置设备属性调用设备服务上报设备属性 等接口传参时会带一个前缀, 具体格式如下:

参数名参数格式举例
属性property_identifier{标准物模型}.{属性名}WxStdSwitch.switch_on
服务service_identifier{标准物模型}.{服务名}WxStdSendFile.WxStdSendFile
事件event_identifier{标准物模型}.{事件名}WxStdHealthDevice.SportsEvent

到这里微信已经将消息推送给我们了,我们结合自己的业务需求,对消息进行保存&展示。

结语

感谢您的阅读,希望能有帮助~

附录:

微信官方对接文档: iot.weixin.qq.com/doc?page=5-…
小程序内完成绑定/解绑的流程 : juejin.cn/post/709192…