让微信能给你设备发送文件消息-微信硬件平台(1)
背景
此篇文章你能获得:了解如何通过微信给自己设备发送文件(充当文件助手)或者链接(充当分享好文快捷入口)等功能;
业务情况
随着用户对 学习机(自研的设备) 不断使用,我们在埋点数据各核心功能使用统计中发现微信的使用率稳居第一,据调研用户主要使用微信上网课、接收文件等。
目标 手机给我们自己的设备传输文件
这里有很多的实现方案
方案1:
自己开发一个文件传输助手,用户可以实现互传,但是手机和设备都装一下文件助手 —— 但是这种方案有些蠢,开发这个文件传输助手还不如我直接在设备上装一个微信呢. 这种方案就不讲了
方案2:
从上面看到用户目前还是比较喜欢通过微信传输文件的习惯的,那可以使用微信开放的接口,通过微信账号跟我们你的设备绑定,然后就可以通过微信给我们的设备推送消息了。
下面我们看看通过微信推送的文件的方案。
一、对接概览
微信硬件平台:https://iot.weixin.qq.com/
对接效果:
本质: 微信和我们设备的sn进行绑定,然后下推一下类似IOT指令,三方服务商接受这个类似IOT指令,然后在设备上完成相应的功能。
二、对接前准备
微信硬件平台上配置的参数:
参数解析:
- appId、secret:小程序注册后给的,需要通过这两个值来获取调用接口的凭证; 还需要通过appid跟微信硬件平台绑定;
- 回调地址:在微信硬件平台上面配置,通过微信给设备推消息的时候,微信会将信息通过回调调用这个回调地址(包括绑定、解绑、消息推送等);
- token:微信硬件平台自动生成的,通过他跟回调的参数进行拼接,对数据的完整性进行校验,判断这个调用是否来自于微信;
- procutID:需要在微信硬件平台上面添加,用于设备绑定。
注意:
要正式投入使用需要在微信硬件平台上面申请上线
三、接入方案
整体流程:
解析:
1、微信推送消息
可以推动文件(pdf、word、txt等)、图片、链接(微信公众号文章,三方分享到微信的文章)、音乐(QQ音乐、网易云音乐等,实际上也是个链接)
2、存储文件
会先将消息存储和文件先存到云上(文件、图片url的下载有效期是1天,链接和音乐理论上是永久有效)
3、消息通知
通过消息回调的方式,回调到你在微信硬件平台,会将消息推送给你,无论是图片、文件、音乐、链接都是统一成链接给你
4、转存文件(可选)
假如是文件的话,文件是有失效时间的,假如文件转存下来,防止丢失
ps:文件都是加过密的,需要解密,加密算法为AEAD_AES_256_GC,解密见:解密文件,AEAD_AES_256_GCM
5、消息下发 可以以你需要的形式将消息发送给用户
- 可以存到库中,用户通过http主动拉去数据,下载用户自主选择下载
- 可以通过iot通知客户端,客户端主动下载
四、微信硬件平台
名词解释:
- product_id: 微信硬件平台对具体某个厂商制造的某款设备产品的标识
- SN: 厂商后台为设备生成的可对外的全局唯一设备标识, product_id范围内唯一
- ilink_im_sdk_id: 设备在微信硬件平台的唯一标识,即微信硬件ID
- ilink_device_ticket: 设备绑定临时身份凭证,五分钟有效期
- ilink_iot_user_id: 设备绑定者在微信硬件平台的身份标识
限制:
- 一个设备只能跟一个用户绑定,即:一个用户可以拥有多个设备,但是一个设备不能属于多个用户
- 文档中有限制文件在30M之内,但是试了一下,超过30M也是可以上传。
五、接口概览
源文件(可用starUML打开):发送消息.mdj
下面部分主要是一些接口概览,如不是实际对接可以不看
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_type | string | 是 | 填写 client_credential |
appid | string | 是 | 小程序/APP唯一凭证,即 AppID,可在「微信公众平台 - 设置 - 开发 设置」页中获得。(需要已经成为开发者,且帐号没有异常状态) |
secret | string | 是 | 小程序/APP唯一凭证密钥,即 AppSecret,获取方式同 appid |
返回值:
属性名 | 类型 | 说明 |
---|---|---|
access_token | string | 获取到的凭证 |
expires_in | number | 凭证有效时间,单位:秒。目前是7200秒之内的值。 |
errcode | number | 错误码, 0为成功 |
errmsg | string | 错误信息 |
2、设备SN注册
接口说明:
每一个设备绑定前,需要到微信硬件平台注册信息
请求地址:
POST https://api.weixin.qq.com/ilink/api/cloud_register_device?access_token=xxx
请求参数:
{
"product_id": 上面申请的procut_id,
"iot_device_list": [
{
"sn": "48210927315672" //设备sn
},
{
"sn": "48210927315673" // 设备sn
}
]
}
属性 | 类型 | 必填 | 说明 |
---|---|---|---|
product_id | number | 是 | 设备产品唯一标识 |
sn | string | 是 | 厂商分配的设备唯一序列号 |
- 单次接口调用,sn个数上线是100,超限将返回错误
- 产品上线前, 只能注册20个sn
返回值:
{
"iot_device_list": [{
"sn": "48210927315672",
"ilink_im_sdk_id": "AAYAABPamWLEhIPAAdkg-oTucvVKrSVfJAZsDEPB5Uw@ilink.im.sdk" // 这个就是在微信上注册后的硬件id
}, {
"sn": "48210927315673",
"ilink_im_sdk_id": "AAYAABPamWI1HBAQAdkg-pDbVJ0xLQXTuee784yUAwk@ilink.im.sdk"
}],
"errcode": 0,
"errmsg": "",
"err_device_list": []
}
3、获取绑定设备凭证
接口说明:
设备绑定时,厂商后台到微信硬件平台拉取该台设备的绑定凭证,传递给小程序端
请求地址:
POST https://api.weixin.qq.com/ilink/api/get_device_ticket?access_token=xxx
请求参数:
{
"ilink_im_sdk_id": "123@ilink.im.sdk",
"user_openid":"xxx"
}
属性 | 类型 | 必填 | 说明 |
---|---|---|---|
ilink_im_sdk_id | string | 是 | 微信硬件平台分配的设备唯一标识 |
user_openid | string | 否 | 绑定设备用户的openid,**选填,填了后将在真正绑定时限制用户只能为所填openid对应用户,假如想限制谁可以绑定设备防止错绑可以加上这个** |
返回值:
{
"errcode":0,
"errmsg":"xxx",
"ilink_device_ticket":"xxx" //ticket
}
属性 | 类型 | 说明 |
---|---|---|
errcode | number | 返回码,0为成功 |
errmsg | string | 错误信息 |
ilink_device_ticket | string | 微信硬件平台设备绑定凭证 |
4、绑定流程
小程序拉起绑定/解绑页
跳转参数都放在 extraData(json 对象)里
-
跳转绑定页
-
页面路径:/pages/discover-new/discover-new
* 传递参数:{"from": "wxprint", "ticket": "xxxxx", "openid": "xxxx"}, from--添加设备标识符, ticket--从ilink后台获取的绑定凭证, openid--用户openid(可选)
绑定成功之后,在微信 -> 设置 -> 设备中出现如下的内容 上面是我自己添加的三个设备,右滑即可对设备进行改名或者直接解绑设备
在微信 -> 设置 -> 设备 中能看到设备即可在聊天框中长按给设备发消息了
5、回调接口
回调的地址:
POST http://$callback_url?signature=xxx×tamp=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/ilink_im_sdk_id/bind
- 设备解绑:topic:/ilink/sys/wechat_iot/productid/ilink_im_sdk_id/unbind
- (标准物联模型)设备发送消息: topic:/ilink/sys/wechat_iot/ productid/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_type | int | 是 | 绑定者类型. 0=普通绑定者, 1=管理员 |
ilink_iot_user_id | string | 是 | 微信硬件平台分配的绑定者身份标识, 上一步生成ticket有传用户openid,就是用户openid;上一步生成ticket无传用户openid,这里就是随机生成的一个用户id |
ilink_im_sdk_id | string | 是 | 微信硬件平台分配的设备唯一标识 |
ilink_device_ticket | string | 否 | 绑定ticket, 厂商在快速对接场景下可以通过该ticket关联微信用户与自有用户. 其他绑定场景不一定有该字段, 请勿使用 |
返回值:
{
"errcode":0,
"errmsg":"xxx",
}
5.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以下文件的发送
下面是发送消息后,推动的对象
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": "AAYAABPamWKRji8aAdkg-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/AAYAABPamWKRji8aAdkg-hgrHdnGvV_nnYrHpl3ux4s@ilink.im.sdk/invoke_device_service"
}
发送QQ音乐:
{
"payload": {
"ilink_im_sdk_id": "AAYAABPamWKRji8aAdkg-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/AAYAABPamWKRji8aAdkg-hgrHdnGvV_nnYrHpl3ux4s@ilink.im.sdk/invoke_device_service"
}
标准物模型参数格式
一个标准物模型的属性,服务或事件名, 有可能和自定义物模型或其他标准物模型重复. 因此在物模管理中的设置设备属性
, 调用设备服务
, 上报设备属性
等接口传参时会带一个前缀, 具体格式如下:
参数名 | 参数格式 | 举例 | |
---|---|---|---|
属性 | property_identifier | {标准物模型}.{属性名} | WxStdSwitch.switch_on |
服务 | service_identifier | {标准物模型}.{服务名} | WxStdSendFile.WxStdSendFile |
事件 | event_identifier | {标准物模型}.{事件名} | WxStdHealthDevice.SportsEvent |
6.解绑
跳转页面说明
页面路径:/pages/delete-devices/delete-devices
传递参数:{"sdkIdList": ["xxx1", "xxx2", "xxx3"]}, 要解绑的设备sdkId列表
回跳参数也放在extraData里:{from: 'wx-iot', "successList": ["xxx1", "xxx2"], "failList": ["xxx3"]}, 里面放的是sdkid列表,表明哪些成功,哪些失败
设备管理页跳转厂商小程序控制面板
微信设备页中,点击设备可支持跳转至厂商小程序中的设备控制页面,厂商需要在产品注册时选择小程序控制面板,并提供:appid 与 page_path,跳转过去所带参数放在extraData里,目前为sdkid参数:{from: 'wx-iot', sdkid: xxxx}
此外需要注意的是如果操作流程是厂商小程序——管理小程序——厂商小程序,则管理小程序会使用wx.navigateBackMiniProgram来跳转,相关参数会放在extraData里,如:
extraData: {
from: 'wx-iot',
sdkid: device.deviceInfo.sdkILinkId,
path: minfo.path
}
结语
感谢您的阅读,希望能有帮助~
附录:
微信官方对接文档: https://iot.weixin.qq.com/doc?page=5-1
新开了掘金号:希望围观 https://juejin.cn/user/255513359550446