فهرست منبع

Merge pull request #192 from larry-dev/master

增加了关于微信智能硬件的一些方式
silenceper 6 سال پیش
والد
کامیت
39e81b45bf
7فایلهای تغییر یافته به همراه384 افزوده شده و 5 حذف شده
  1. 116 0
      device/authorize.go
  2. 106 0
      device/bind.go
  3. 60 0
      device/device.go
  4. 9 0
      device/message.go
  5. 76 0
      device/qrcode.go
  6. 11 5
      message/message.go
  7. 6 0
      wechat.go

+ 116 - 0
device/authorize.go

@@ -0,0 +1,116 @@
+package device
+
+import (
+	"encoding/json"
+	"fmt"
+
+	"github.com/silenceper/wechat/util"
+)
+
+const (
+	// DeviceAdd 添加设备标识
+	DeviceAdd = iota
+	// DeviceUpgrade 更新设备标识
+	DeviceUpgrade
+)
+
+type reqDeviceAuthorize struct {
+	// 设备id的个数
+	DeviceNum string `json:"device_num"`
+	// 设备id的列表,json的array格式,其size必须等于device_num
+	DeviceList []ReqDevice `json:"device_list"`
+	// 请求操作的类型,限定取值为:0:设备授权(缺省值为0) 1:设备更新(更新已授权设备的各属性值)
+	OpType string `json:"op_type,omitempty"`
+	// 设备的产品编号(由微信硬件平台分配)。可在公众号设备功能管理页面查询。
+	//当 op_type 为‘0’,product_id 为‘1’时,不要填写 product_id 字段(会引起不必要错误);
+	//当 op_typy 为‘0’,product_id 不为‘1’时,必须填写 product_id 字段;
+	//当 op_type 为 1 时,不要填写 product_id 字段。
+	ProductID string `json:"product_id,omitempty"`
+}
+
+//ReqDevice 设备授权实体
+type ReqDevice struct {
+	// 设备的 device id
+	ID string `json:"id"`
+	// 设备的mac地址 格式采用16进制串的方式(长度为12字节),
+	// 不需要0X前缀,如: 1234567890AB
+	Mac string `json:"mac"`
+	//  支持以下四种连接协议:
+	//	android classic bluetooth – 1
+	//	ios classic bluetooth – 2
+	//	ble – 3
+	//	wifi -- 4
+	//	一个设备可以支持多种连接类型,用符号"|"做分割,客户端优先选择靠前的连接方式(优先级按|关系的排序依次降低),举例:
+	//	1:表示设备仅支持andiod classic bluetooth 1|2:表示设备支持andiod 和ios 两种classic bluetooth,但是客户端优先选择andriod classic bluetooth 协议,如果andriod classic bluetooth协议连接失败,再选择ios classic bluetooth协议进行连接
+	//	(注:安卓平台不同时支持BLE和classic类型)
+	ConnectProtocol string `json:"connect_protocol"`
+	//auth及通信的加密key,第三方需要将key烧制在设备上(128bit),格式采用16进制串的方式(长度为32字节),不需要0X前缀,如: 1234567890ABCDEF1234567890ABCDEF
+	AuthKey string `json:"auth_key"`
+	// 断开策略,目前支持: 1:退出公众号页面时即断开连接 2:退出公众号之后保持连接不断开
+	CloseStrategy string `json:"close_strategy"`
+	//连接策略,32位整型,按bit位置位,目前仅第1bit和第3bit位有效(bit置0为无效,1为有效;第2bit已被废弃),且bit位可以按或置位(如1|4=5),各bit置位含义说明如下:
+	//1:(第1bit置位)在公众号对话页面,不停的尝试连接设备
+	//4:(第3bit置位)处于非公众号页面(如主界面等),微信自动连接。当用户切换微信到前台时,可能尝试去连接设备,连上后一定时间会断开
+	ConnStrategy string `json:"conn_strategy"`
+	// auth version,设备和微信进行auth时,会根据该版本号来确认auth buf和auth key的格式(各version对应的auth buf及key的具体格式可以参看“客户端蓝牙外设协议”),该字段目前支持取值:
+	// 0:不加密的version
+	// 1:version 1
+	AuthVer string `json:"auth_ver"`
+	// 表示mac地址在厂商广播manufature data里含有mac地址的偏移,取值如下:
+	// -1:在尾部、
+	// -2:表示不包含mac地址 其他:非法偏移
+	ManuMacPos string `json:"manu_mac_pos"`
+	// 表示mac地址在厂商serial number里含有mac地址的偏移,取值如下:
+	// -1:表示在尾部
+	// -2:表示不包含mac地址 其他:非法偏移
+	SerMacPost string `json:"ser_mac_post"`
+	// 精简协议类型,取值如下:计步设备精简协议:1 (若该字段填1,connect_protocol 必须包括3。非精简协议设备切勿填写该字段)
+	BleSimpleProtocol string `json:"ble_simple_protocol,omitempty"`
+}
+
+//ResBaseInfo 授权回调实体
+type ResBaseInfo struct {
+	BaseInfo struct {
+		DeviceType string `json:"device_type"`
+		DeviceID   string `json:"device_id"`
+	} `json:"base_info"`
+}
+
+// 授权回调根信息
+type resDeviceAuthorize struct {
+	util.CommonError
+	Resp []ResBaseInfo `json:"resp"`
+}
+
+// DeviceAuthorize 设备授权
+func (d *Device) DeviceAuthorize(devices []ReqDevice, opType int, product string) (res []ResBaseInfo, err error) {
+	var accessToken string
+	accessToken, err = d.GetAccessToken()
+	if err != nil {
+		return
+	}
+
+	uri := fmt.Sprintf("%s?access_token=%s", uriAuthorize, accessToken)
+	req := reqDeviceAuthorize{
+		DeviceNum:  fmt.Sprintf("%d", len(devices)),
+		DeviceList: devices,
+		OpType:     fmt.Sprintf("%d", opType),
+		ProductID:  product,
+	}
+	var response []byte
+	response, err = util.PostJSON(uri, req)
+	if err != nil {
+		return nil, err
+	}
+	var result resDeviceAuthorize
+	err = json.Unmarshal(response, &result)
+	if err != nil {
+		return
+	}
+	if result.ErrCode != 0 {
+		err = fmt.Errorf("DeviceAuthorize Error , errcode=%d , errmsg=%s", result.ErrCode, result.ErrMsg)
+		return
+	}
+	res = result.Resp
+	return
+}

+ 106 - 0
device/bind.go

@@ -0,0 +1,106 @@
+package device
+
+import (
+	"encoding/json"
+	"fmt"
+
+	"github.com/silenceper/wechat/util"
+)
+
+// ReqBind 设备绑定解绑共通实体
+type ReqBind struct {
+	Ticket   string `json:"ticket,omitempty"`
+	DeviceID string `json:"device_id"`
+	OpenID   string `json:"openid"`
+}
+type resBind struct {
+	BaseResp util.CommonError `json:"base_resp"`
+}
+
+// Bind 设备绑定
+func (d *Device) Bind(req ReqBind) (err error) {
+	var accessToken string
+	if accessToken, err = d.GetAccessToken(); err != nil {
+		return
+	}
+	uri := fmt.Sprintf("%s?access_token=%s", uriBind, accessToken)
+	var response []byte
+	if response, err = util.PostJSON(uri, req); err != nil {
+		return
+	}
+	var result resBind
+	if err = json.Unmarshal(response, &result); err != nil {
+		return
+	}
+	if result.BaseResp.ErrCode != 0 {
+		err = fmt.Errorf("DeviceBind Error , errcode=%d , errmsg=%s", result.BaseResp.ErrCode, result.BaseResp.ErrMsg)
+		return
+	}
+	return
+}
+
+// Unbind 设备解绑
+func (d *Device) Unbind(req ReqBind) (err error) {
+	var accessToken string
+	if accessToken, err = d.GetAccessToken(); err != nil {
+		return
+	}
+	uri := fmt.Sprintf("%s?access_token=%s", uriUnbind, accessToken)
+	var response []byte
+	if response, err = util.PostJSON(uri, req); err != nil {
+		return
+	}
+	var result resBind
+	if err = json.Unmarshal(response, &result); err != nil {
+		return
+	}
+	if result.BaseResp.ErrCode != 0 {
+		err = fmt.Errorf("DeviceBind Error , errcode=%d , errmsg=%s", result.BaseResp.ErrCode, result.BaseResp.ErrMsg)
+		return
+	}
+	return
+}
+
+// CompelBind 强制绑定用户和设备
+func (d *Device) CompelBind(req ReqBind) (err error) {
+	var accessToken string
+	if accessToken, err = d.GetAccessToken(); err != nil {
+		return
+	}
+	uri := fmt.Sprintf("%s?access_token=%s", uriCompelBind, accessToken)
+	var response []byte
+	if response, err = util.PostJSON(uri, req); err != nil {
+		return
+	}
+	var result resBind
+	if err = json.Unmarshal(response, &result); err != nil {
+		return
+	}
+	if result.BaseResp.ErrCode != 0 {
+		err = fmt.Errorf("DeviceBind Error , errcode=%d , errmsg=%s", result.BaseResp.ErrCode, result.BaseResp.ErrMsg)
+		return
+	}
+	return
+}
+
+// CompelUnbind 强制解绑用户和设备
+func (d *Device) CompelUnbind(req ReqBind) (err error) {
+	var accessToken string
+	if accessToken, err = d.GetAccessToken(); err != nil {
+		return
+	}
+	uri := fmt.Sprintf("%s?access_token=%s", uriCompelUnbind, accessToken)
+	var response []byte
+	if response, err = util.PostJSON(uri, req); err != nil {
+		return
+	}
+	var result resBind
+	if err = json.Unmarshal(response, &result); err != nil {
+		return
+	}
+	if result.BaseResp.ErrCode != 0 {
+		err = fmt.Errorf("DeviceBind Error , errcode=%d , errmsg=%s", result.BaseResp.ErrCode, result.BaseResp.ErrMsg)
+		return
+	}
+	return
+}

+ 60 - 0
device/device.go

@@ -0,0 +1,60 @@
+package device
+
+import (
+	"encoding/json"
+	"fmt"
+
+	"github.com/silenceper/wechat/context"
+	"github.com/silenceper/wechat/util"
+)
+
+const (
+	uriAuthorize    = "https://api.weixin.qq.com/device/authorize_device"
+	uriQRCode       = "https://api.weixin.qq.com/device/create_qrcode"
+	uriVerifyQRCode = "https://api.weixin.qq.com/device/verify_qrcode"
+	uriBind         = "https://api.weixin.qq.com/device/bind"
+	uriUnbind       = "https://api.weixin.qq.com/device/unbind"
+	uriCompelBind   = "https://api.weixin.qq.com/device/compel_bind"
+	uriCompelUnbind = "https://api.weixin.qq.com/device/compel_unbind"
+	uriState        = "https://api.weixin.qq.com/device/get_stat"
+)
+
+//Device struct
+type Device struct {
+	*context.Context
+}
+
+//NewDevice 实例
+func NewDevice(context *context.Context) *Device {
+	device := new(Device)
+	device.Context = context
+	return device
+}
+
+// ResDeviceState 设备状态响应实体
+type ResDeviceState struct {
+	util.CommonError
+	Status     int    `json:"status"`
+	StatusInfo string `json:"status_info"`
+}
+
+// State 设备状态查询
+func (d *Device) State(device string) (res ResDeviceState, err error) {
+	var accessToken string
+	if accessToken, err = d.GetAccessToken(); err != nil {
+		return
+	}
+	uri := fmt.Sprintf("%s?access_token=%s&device_id=%s", uriState, accessToken, device)
+	var response []byte
+	if response, err = util.HTTPGet(uri); err != nil {
+		return
+	}
+	if err = json.Unmarshal(response, &res); err != nil {
+		return
+	}
+	if res.ErrCode != 0 {
+		err = fmt.Errorf("DeviceState Error , errcode=%d , errmsg=%s", res.ErrCode, res.ErrMsg)
+		return
+	}
+	return
+}

+ 9 - 0
device/message.go

@@ -0,0 +1,9 @@
+package device
+
+//MsgDevice 设备消息响应
+type MsgDevice struct {
+	DeviceType string
+	DeviceID   string
+	SessionID  string
+	OpenID     string
+}

+ 76 - 0
device/qrcode.go

@@ -0,0 +1,76 @@
+package device
+
+import (
+	"encoding/json"
+	"fmt"
+
+	"github.com/silenceper/wechat/util"
+)
+
+//ResCreateQRCode 获取二维码的返回实体
+type ResCreateQRCode struct {
+	util.CommonError
+	DeviceNum int `json:"device_num"`
+	CodeList  []struct {
+		DeviceID string `json:"device_id"`
+		Ticket   string `json:"ticket"`
+	} `json:"code_list"`
+}
+
+// CreateQRCode 获取设备二维码
+func (d *Device) CreateQRCode(devices []string) (res ResCreateQRCode, err error) {
+	var accessToken string
+	if accessToken, err = d.GetAccessToken(); err != nil {
+		return
+	}
+	uri := fmt.Sprintf("%s?access_token=%s", uriQRCode, accessToken)
+	req := map[string]interface{}{
+		"device_num":     len(devices),
+		"device_id_list": devices,
+	}
+	var response []byte
+	if response, err = util.PostJSON(uri, req); err != nil {
+		return
+	}
+	if err = json.Unmarshal(response, &res); err != nil {
+		return
+	}
+	if res.ErrCode != 0 {
+		err = fmt.Errorf("DeviceCreateQRCode Error , errcode=%d , errmsg=%s", res.ErrCode, res.ErrMsg)
+		return
+	}
+	return
+}
+
+//ResVerifyQRCode 验证授权结果实体
+type ResVerifyQRCode struct {
+	util.CommonError
+	DeviceType string `json:"device_type"`
+	DeviceID   string `json:"device_id"`
+	Mac        string `json:"mac"`
+}
+
+// VerifyQRCode 验证设备二维码
+func (d *Device) VerifyQRCode(ticket string) (res ResVerifyQRCode, err error) {
+	var accessToken string
+	if accessToken, err = d.GetAccessToken(); err != nil {
+		return
+	}
+	uri := fmt.Sprintf("%s?access_token=%s", uriVerifyQRCode, accessToken)
+	req := map[string]interface{}{
+		"ticket": ticket,
+	}
+	fmt.Println(req)
+	var response []byte
+	if response, err = util.PostJSON(uri, req); err != nil {
+		return
+	}
+	if err = json.Unmarshal(response, &res); err != nil {
+		return
+	}
+	if res.ErrCode != 0 {
+		err = fmt.Errorf("DeviceCreateQRCode Error , errcode=%d , errmsg=%s", res.ErrCode, res.ErrMsg)
+		return
+	}
+	return
+}

+ 11 - 5
message/message.go

@@ -1,6 +1,9 @@
 package message
 
-import "encoding/xml"
+import (
+	"encoding/xml"
+	"github.com/silenceper/wechat/device"
+)
 
 // MsgType 基本消息类型
 type MsgType string
@@ -148,10 +151,13 @@ type MixMessage struct {
 	UnionID             string `xml:"UnionId"`
 
 	// 内容审核相关
-	IsRisky 		    bool `xml:"isrisky"`
-	ExtraInfoJSON 		string `xml:"extra_info_json"`
-	TraceID 			string `xml:"trace_id"`
-	StatusCode          int `xml:"status_code"`
+	IsRisky       bool   `xml:"isrisky"`
+	ExtraInfoJSON string `xml:"extra_info_json"`
+	TraceID       string `xml:"trace_id"`
+	StatusCode    int    `xml:"status_code"`
+
+	//设备相关
+	device.MsgDevice
 }
 
 //EventPic 发图事件推送

+ 6 - 0
wechat.go

@@ -1,6 +1,7 @@
 package wechat
 
 import (
+	"github.com/silenceper/wechat/device"
 	"net/http"
 	"sync"
 
@@ -111,3 +112,8 @@ func (wc *Wechat) GetQR() *qr.QR {
 func (wc *Wechat) GetMiniProgram() *miniprogram.MiniProgram {
 	return miniprogram.NewMiniProgram(wc.Context)
 }
+
+// GetDevice 获取智能设备的实例
+func (wc *Wechat) GetDevice() *device.Device {
+	return device.NewDevice(wc.Context)
+}