• 本部分源码放置于

3.4.1 界面展示

bind

图23 设备绑定页面

noconnect
图24 状态未连接

新用户在设备绑定页面绑定设备,点击添加按钮扫描设备上印刷的设备绑定二维码即可绑定设备。

主页面由上到下显示设备编号、绑定日期和至今时间、当前状态以及识别按钮。当前状态分为未连接、未锁止、锁止中三种,未连接提醒用户检查设备供电及网络情况,未锁止表示设备已开箱,在未连接或未锁止时禁止用户执行开锁。下方的识别按钮点击后进入指纹识别,并可以变化图标展示双方的识别状态。若双方识别成功发出解锁命令,页面弹出正在解锁模态框并且手机会震动提示。

nolock

图25 状态未锁止

locking
图26 状态锁止中

unlocking
图27 正在开锁

用户可通过点击页面上的齿轮图标,弹出选项菜单,进行注销设备和查看设备历史纪录的操作,历史纪录页面显示设备上线离线、上锁开锁的具体时间。

menu

图28 选项菜单

history
图29 设备历史记录

3.4.2 程序说明

当用户进入小程序后,全局入口获取临时登录凭证code并发送请求至服务器后端程序RESTful接口login,若得到返回JSON值 'exists':1 ,则将得到的所有数据放入小程序本地数据缓存中并跳转至主页面indexpage,若值为0,则跳转至设备绑定页面addpage。

全局入口函数

setkey:function setkey () {
    var that = this
    // wx.clearStorageSync()
    console.log('setkey function')

    // 调用获取临时登录凭证code
    wx.login({
    success: function (res) {
    if (res.code) {
        // 发出POST请求
        wx.request({
        // url: 'http://127.0.0.1:5000/login',
        url: 'https://**************/wx/login',            
        method: 'POST',
        header: { 'content-type': 'application/json' },
        data: {
            code: res.code
        },
        success: function (sres) {
            if (sres.data != '') {
                var sdata = sres.data
                if (sdata['exists'] == 1) {
                    // 数据放入Storage
                    wx.setStorageSync('lockno', sdata['lockno'])
                    wx.setStorageSync('morf', sdata['morf'])
                    wx.setStorageSync('adate', sdata['adate'])
                    wx.setStorageSync('key', sdata['key'])
                    wx.setStorageSync('pwd', sdata['pwd'])

                    // 跳转主页面
                    wx.reLaunch({
                        url: '../indexpage/index'
                    })
                }
                else {
                    // 跳转设备绑定页面
                    console.log('nonono')
                    wx.reLaunch({
                    url: '../addpage/addpage'
                    })
                }
            }
            else
                console.log('server error!')
        }
        })
    } else {
        console.log('wxlogin error!' + res.errMsg)
    }
    }
})
}

在设备绑定页面用户通过扫描设备上印刷的识别二维码完成用户和设备的绑定,触发添加按钮后,程序调用小程序提供的wx.scanCode扫码API,扫码成功后,扫描到的编码信息和临时登录凭证code一起发送请求至服务器后端程序RESTful接口logup,服务器端对数据解码成功后将用户和设备的对应关系写入数据库user2lock表完成绑定,若出错则返回相应的错误代码。成功绑定会在延迟200ms后跳转回全局入口。

设备绑定函数

add: function () {
  // 调用扫码API
  wx.scanCode({
    onlyFromCamera: true,
    success(qres) {
        console.log(qres)

        wx.login({
        success: function(res) {
            if (res.code) {
                // 发出POST请求
                wx.request({
                // url: 'http://127.0.0.1:5000/logup',
                url: 'http://**************/wx/logup',
                method: 'POST',
                header: {
                    'content-type': 'application/json'
                },
                data: {
                    code: res.code,
                    qrcode: qres.result
                },
                success: function(sres) {
                if (sres.data == 1) {
                    setTimeout(app.setkey, 200)

                } else {
                    if (sres.data == 2) {
                    wx.showToast({
                        title: '设备已被注册',
                        icon: 'none'
                    })
                    }
                    if (sres.data == 0) {
                    wx.showToast({
                        title: '检查二维码有错误',
                        icon: 'none'
                    })
                    } 
                }
                }
                })
            } else {
                console.log('wxlogin error!' + res.errMsg)
            }
        }
        })
    }
  })
}

主页面的逻辑,首先是判断设备是否支持指纹识别和建立MQTT协议连接。小程序的指纹识别API接口是通过SOTER 生物认证提供的,腾讯于2015年开始制定生物认证平台与标准,目前SOTER已经用于微信指纹支付、微信公众号/小程序指纹授权接口等场景,并得到了长期的验证,稳定可靠。[ ]小程序的MQTT协议连接采用引入paho-mqtt-min.js来实现,Paho 项目是Eclipse基金会的一个开源的 MQTT 客户端项目,提供多种语言的 MQTT 客户端实现,[ ]在这里通过小程序允许的WSS(WebSocket/SSL)连接方式建立MQTT连接,WebSocket 是一种在单个 TCP 连接上进行全双工通讯的协议,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。在进行指纹识别前程序会判断设备的状态是否可以开锁,同时另一方的校验情况也能通过MQTT协议订阅实时获取并通过图标的变化在界面上展现出来。

支持指纹识别检测

wx.checkIsSupportSoterAuthentication({
  success(res) {
    for (var i in res.supportMode) {
      if (res.supportMode[i] == 'fingerPrint') {
        console.log("支持指纹识别", res.supportMode[i]);
        that.data.isfingerPrint = true
      }
    }
  }
})

建立MQTT协议连接

var key = wx.getStorageSync('key')
var pwd = wx.getStorageSync('pwd')

// var client = new Paho.Client("172.20.0.145", 8083, "clientId");
var client = new Paho.Client("wss://************/mqtt", key);

client.onConnectionLost = that.onConnectionLost;
client.onMessageArrived = that.onMessageArrived;

var willmsg = new Paho.Message(key);
willmsg.destinationName = '/unauth';

client.connect({
  useSSL: true,
  // userName: 'user',
  userName: wx.getStorageSync('key'),
  // password: 'pwd',
  password: wx.getStorageSync('pwd'),
  cleanSession: true,
  keepAliveInterval: 30,
  willMessage: willmsg,
  onSuccess: function() {
    console.log("onConnect");
    that.data.client = client
    client.subscribe(that.buildTopic('#'));
    // that.publish('ping', '1')
  }
});

识别开锁函数

FingerPrint: function() {
  var that = this
  if (this.checkIsFingerPrint) {
    wx.startSoterAuthentication({
      requestAuthModes: ['fingerPrint'],
      challenge: 'sakura',
      authContent: '请用指纹',
      success(res) {
        console.log("识别成功", res)
        var morf = wx.getStorageSync('morf')
        that.publish(morf, '1')
      },
      fail(res) {
        console.log("识别失败", res)
        wx.showToast({
          title: '识别失败',
          icon: 'none'
        })
      }
    })
  }
}

订阅信息处理

onMessageArrived: function onMessageArrived(message) {
  console.log("onMessageArrived: [" + message.destinationName + "] " + message.payloadString);
  var topic = message.destinationName
  var payload = message.payloadString
  var topic_part = topic.split('/')
  // console.log(topic_part)
  if (topic_part[2] == 'statu') {
    this.setData({
      statu: payload
    })
  }
  if (topic_part[2] == 'm') {
    this.setData({
      m_button: false,
      m_verify: true
    })
    this.display_check()
  }
  if (topic_part[2] == 'f') {
    this.setData({
      f_button: false,
      f_verify: true
    })
    this.display_check()
  }
}