初始化 MeshController

::
//在 app.js 中初始化一下代码 MeshController.init()

设置网络节点数据更新回调,连接状态回调

  MeshController=getApp().getMeshController()

    MeshController.nodeDataObserver.set(function () {
        let devices = MeshController.nodes()
        //数据更新
        that.setData({devices})

        console.log('notify data changed')
    });

    MeshController._setConnStateChangelistener(function (res) {
    // 设备连接状态更新回调
        console.log('connected:'+JSON.stringify(res))
        if (res.connected) {
           Loading.showToast('connected');
            that.setData({
                connected: true,
                deviceName: res.name,
                connState: 'disconnect'
            })
        } else {
            Loading.showToast('disconnected');
            that.setData({
                connected: false,
                deviceName: 'Bluex Mesh',
                connState: 'connect'
            })
        }
    });
// 下拉刷新数据
MeshController.Provisioner.reSlectNodes();

开始扫描

 //扫描未入网设备
MeshController.scanUnprovDevice(function (device) {
                addDevice(device)
            }).then(res => {
            }).catch(reason => {
             Loading.hideLoading()
                showError(reason)
            })

//扫描已入网设备
  let params = {}
            params.cb = function (device) {
                addDevice(device)
            }
            Loading.showLoading('scanning')
            MeshController.scanProxyNode(params).then(res => {
            }).catch(reason => {
                Loading.hideLoading()
                showError(reason)
            })

连接设备

MeshController.stopScan().then(res => {
         Loading.showLoading(title);
         let device =  bluetoothDevice;
         let conn2ProxyNode=that.data.uuid[0]===MESH_PROXY_UUID[0]//是否连接入网的节点
         MeshController.connect({device},conn2ProxyNode).then(res => {
             //  连接设备成功
             Loading.hideLoading()
             switch (that.data.uuid[0]) {
                 case MESH_PROVISION_UUID[0]:
                     wx.navigateTo({
                         url: '../provision/provisioner?device=' + JSON.stringify(bluetoothDevice)
                     })
                     break;
                 case MESH_PROXY_UUID[0]:
                     MeshController.setCurNode(bluetoothDevice.name)
                     wx.navigateBack()
                     break;
                 default:break;
             }
         }).catch(reason => {
             Loading.hideLoading()
             // 连接异常捕获 具体请参考errCode.js中的定义 这里的处理方式 ->重新连接
           conn('reconnecting')
             // showError(reason)
         })

     }).catch(reason => {
         showError(reason)
     });

开始入网设备

//例如provisioner.js 部分代码如下显示
  //注册入网过程回调
  MeshController.setMeshProvisioningHandler({
        onStartInvite: function (res) {
            // that.sendingProvisionInvite()
            that.setProvisionState(res)

        },
        onReceivedCapabilities: function (res) {
            that.setProvisionState(res)
        },
        onProvisionStart: function (res) {
            that.setProvisionState(res)

        },
        onSendingPublicKey: function (res) {
            that.setProvisionState(res)
        },
        onReceivedPublicKey: function (res) {
            that.setProvisionState(res)

        },
        onSendConfirmData: function (res) {
            that.setProvisionState(res)
        },
        onReceivedConfirm: function (res) {
            that.setProvisionState(res)

        },
        onSendConfirmRandom: function (res) {
            that.setProvisionState(res)
        },
        onReceivedConfirmRandom: function (res) {
            that.setProvisionState(res)

        },

        onSendingProvisionData: function (res) {

            that.setProvisionState(res)
        },
        onReceivedProvisionComplete: function (res) {
            that.setProvisionState(res).disconn()
        },
       })

         //注册Mesh 消息回调(已经入网,后续的消息包括配置消息,OnOff消息)
         MeshController.registerMeshMessageHandler(KEY, function (res) {
            let state
            switch (res.opCode) {
                case OPCODE.SEG_ACK://sending block ack
                    state = {type: TYPE.WRITE, status: 'Sending BlockAcknowledgement'};
                    break;
                case OPCODE.SEG_RESENT://resend Segment
                    state = {type: TYPE.WRITE, status: 'Rsending Sgement'};
                    break;
                case OPCODE.CONFIG_COMPOSITION_DATA_GET:
                    state = {type: TYPE.WRITE, status: 'Sending CompositionDataGet'};
                    break;
                case OPCODE.CONFIG_APPKEY_ADD:
                    state = {type: TYPE.WRITE, status: 'Sending ConfigAppKeyAdd'};
                    break;
                case OPCODE.CONFIG_MODEL_APP_BIND:
                    state = {type: TYPE.WRITE, status: 'Sending ConfigModelAppkeyBind'};
                    break;
                case OPCODE.CONFIG_MODEL_SUBSCRIPTION_ADD:
                    state = {type: TYPE.WRITE, status: 'Sending ConfigSubsctiptionAdd'};
                    break;
                case OPCODE.CONFIG_COMPOSITION_DATA_STATUS:
                    state = {type: TYPE.RECEIVED, status: 'Receiving CompositionDataStatus'}
                    sendingConfigAppKeyAdd();
                    break;
                case OPCODE.CONFIG_APPKEY_STATUS:
                    state = {type: TYPE.RECEIVED, status: 'Receive ConfigAppkeyStatus'};
                    if (res.statusMessage.StatusCode == 0) {
                        initwillBindKeyModel(that);
                        nextMessageSend();
                    }
                    break
                case OPCODE.CONFIG_MODEL_APP_STATUS:
                    state = {type: TYPE.RECEIVED, status: 'Receive ConfigModelAppkeyBindStatus'};
                    nextMessageSend();
                    break;
                case OPCODE.CONFIG_MODEL_SUBSCRIPTION_STATUS:
                    state = {type: TYPE.RECEIVED, status: 'Receive SubscriptionStatus'};
                    nextMessageSend();
                    break;
                default:
                    break;
            }
            if (state) {
               //刷新界面
                that.setProvisionState(state)
            }
        }
    )

     function nextMessageSend() {
        let msg = that.data.queue.pop()
        if (msg) {
            sendMessage(msg)
            that.pageScrollToBottom();
        } else {
        //  配置消息发送完毕  退出当前界面,
            getApp().switchTab('network')
        }

    }

        //初始化需要绑定appkey,订阅组地址的model
        function initwillBindKeyModel(context) {
           let currentNode = MeshController.getCurNode();
       let dst = currentNode.unicastAddress;
        let queue = context.data.queue;
       let groups = MeshController.getGroups();
       currentNode.elements.map((element, index, self) => {
        element.models.map(model => {
        let modelId = parseInt(model.modelId, 16)
        if (modelId === 0x1000) {
            // model绑定appkey
            let appKeyIndex = 0
            queue.push(new ConfigModelAddKeyBind(dst, element.elementAddress, appKeyIndex, modelId))
            // mdoel 订阅组地址 也就是Groups.js 界面的分组控制OnOff
            let subscriptionAddress = groups.length > 0 ? groups[0].address : 0xc000
            queue.push(new ConfigModelSubscriptionAdd(dst, element.elementAddress, subscriptionAddress, modelId))

        }

    })
})

}

控制设备

 //注册消息回调
 (function initMeshMsgHandler() {
    MeshController.registerMeshMessageHandler(getPageKey(), function (res) {
        switch (res.opCode) {
            case CONFIG_COMPOSITION_DATA_GET:
                break;

            case CONFIG_NODE_RESET_STATUS:
                // if connected device  is reset  should  disconnect
                let isCurNodeReset = MeshController.isCurNodeReset()
                console.debug('isCurNodeReset:'+isCurNodeReset)
                if (isCurNodeReset) {
                    setTimeout(res => {
                        MeshController.disconnect().then(res => {
                            getApp().switchMain()
                        }).catch(reason => {
                        })
                    }, 500)
                } else {
                    getApp().switchMain()
                }

                break;
            case CONFIG_COMPOSITION_DATA_STATUS:
                setupNodeInfo(that)
                break;
            case CONFIG_APPKEY_STATUS:
                nextMessage()
                break;
            case CONFIG_MODEL_APP_STATUS:
                nextMessage()
                break;
            case CONFIG_MODEL_SUBSCRIPTION_STATUS:
                nextMessage()
                break;
            case GENERIC_ON_OFF_STATUS:
              //收到设备回复OnOff消息 刷新界面
                that.updateOnOffModelState(res.statusMessage)
                break;

        }


       })


  })();


    function sendMessage(message) {
     //判断设备是否已连接
        if (MeshController.connected()) {
        MeshController.sendMeshMessage(message).catch(reason => {
            console.error('sendMessage error:' + reason)
        });
    } else {
       //连接设备
    }
}
// 亮灯
 sendMessage(new GenericOnOffSetAck(1, _seqNum(), this.curElementAddress))
 // 灭灯
  sendMessage(new GenericOnOffSetAck(0, _seqNum(), this.curElementAddress))

创建分组

    //group.js 中部分代码
   onAddGroupClick: function () {
    let that = this
    let isShow = that.data.showModals
    if (!isShow) {
        that.setData({showModals: true})
    }
}

订阅分组

//ConfigModelSubscriptionAdd
MeshController.sendMeshMessage(new ConfigModelSubscriptionAdd(dst, element.elementAddress, subscriptionAddress, modelId)).catch(res => {
         if (res.errCode === DEVICE_NOT_CONN) {
             getApp().showToast(res.reason)
             setTimeout(() => {
                 that.route()
             }, 500)
         }
     })

移除节点(使设备状态恢复初始状态,也就是未入网设备)

//ConfigNodeReset
MeshController.sendMeshMessage(new ConfigNodeReset(app.getSelectedNode().unicastAddress)).catch(res => {
        if (res.errCode === DEVICE_NOT_CONN) {
            getApp().showToast(res.reason)
            setTimeout(() => {
                that.route()
            }, 500)
        }
    })

云函数使用

//例如 将入网设备存储至云端   CloudfuncController.js 部分代码如下显示
let CloudController = require('./CloudfuncController').getInstance()
//每一个用户拥有唯一的openid,小程序云自动生成的无需创建
CloudController.insertNode(node,openid)
//其它云函数具体使用请查看Demo中的使用

扫码共享当前已经入网数据

    // qrcode.js 中的部分代码
  wx.scanCode({
        onlyFromCamera: true,
        scanType: ['qrCode']
        , success(res) {
            let obj = JSON.parse(res.result)
            if (obj.openid) {
               getApp().getMeshController().bindUser(obj.openid).then(res=>{
                   getApp().switchTab('network')
                })
            } else {
            }

        }
    })
}

log 输出

MeshController.setLogger({
     DEBUG:function (tag,info) {
         console.debug(tag+'\n'+info)
     },
     ERROR:function (tag,info) {
         console.error(tag+'\n'+info)
     }
 });

关闭数据及时刷新监听 具体使用细节请查看小程序文档云函数使用

MeshController.CloudController.closeNodesWatch()
    MeshController. CloudController.closeGroupWatch()
    MeshController.CloudController.closeProCfgWatch()

云函数定义

  //以下是插入节点到云端代码    路径wxapp_blemesh/cloud/insertNode.js
 // 云函数入口文件
const cloud = require('wx-server-sdk')

cloud.init()
let db = cloud.database()
// 云函数入口函数
exports.main = async (event, context) => {
const wxContext = cloud.getWXContext()
event._openid = event.openid
//插入数据之前查询数据库中是否存在该节点
let rsl = await db.collection('provisioned_nodes').where({name: event.node.name}).get()
if (rsl.data&&rsl.data.length>0) {
    let provisionedNode = rsl.data[0]
    //移除已存在节点
    await db.collection('provisioned_nodes').where({name: provisionedNode.name}).remove()
    //移除on_off_model_state 表中对应记录
    await  db.collection('on_off_model_state').where({name: provisionedNode.name}).remove()
}

return await db.collection('provisioned_nodes').add({
    data: event.node
})
 }

创建云函数数据表

../_images/create_colection_cloud.png

在云控制台添加集合

groups
on_off_model_state
provision_config
provisioned_nodes
src
user

导入默认的入网配置

集合创建完毕后需要在集合“provision_config”导入一份默认的入网配置json文件
步骤1:将以下json文本拷贝至文本文件中,然后重命名为xxx.json
{"_id":"5ac1f101-8096-4e3d-a3ff-7d229f309ca6","unicastAddress":"0001","src":32767.0,"networKey":["8b19ac31d58b124c946209b5db1021b9"],"appKeys":["000102030405060708090A0B0C0D0E0F"],"keyIndex":"0000","flags":"00","ivIndex":"00000000","seq_num":0}

步骤2 在云开发控制台中选中provision_config 集合,点击导入,选择之前重命名的xxx.json 这样默认的入网配置就算完成了

常见问题

1 初次使用时如果android手机版本大于等于5.0,请务必开启手机定位权限,也就是在手机设置中开启微信定位权限,不然会造成无法扫描到mesh 设备,

2 如发现扫描不到设备,请确认是否开启蓝牙

3 如果发现小程序显示一直在连接设备,或者连接设备失败,请尝试退出当前页面,再次扫描设备进行连接操作.

  • [Q] 入网过程中卡在以下界面?
../_images/wxapp_interrupt.jpg