wzh 2 år sedan
förälder
incheckning
11b1d8cda5
100 ändrade filer med 31943 tillägg och 116 borttagningar
  1. 65 0
      api/device/device.js
  2. 4 3
      config.js
  3. 6 2
      main.js
  4. 14 3
      manifest.json
  5. 27 8
      pages.json
  6. 633 0
      pages/device/detail.vue
  7. 313 0
      pages/device/index.vue
  8. 162 0
      pages/device/scan.vue
  9. 32 23
      pages/login.vue
  10. 11 11
      pages/mine/about/index.vue
  11. 10 10
      pages/mine/index.vue
  12. 241 0
      pages/wifi/index.vue
  13. 35 53
      pages/work/index.vue
  14. BIN
      static/images/banner/banner01.jpg
  15. BIN
      static/images/device/device.png
  16. 9 2
      store/modules/user.js
  17. 3 1
      uni.scss
  18. 27 0
      uni_modules/mqtt-packet/.github/workflows/ci.yml
  19. 27 0
      uni_modules/mqtt-packet/CONTRIBUTING.md
  20. 13 0
      uni_modules/mqtt-packet/LICENSE.md
  21. 491 0
      uni_modules/mqtt-packet/README.md
  22. 26 0
      uni_modules/mqtt-packet/benchmarks/generate.js
  23. 51 0
      uni_modules/mqtt-packet/benchmarks/generateNet.js
  24. 20 0
      uni_modules/mqtt-packet/benchmarks/parse.js
  25. 49 0
      uni_modules/mqtt-packet/benchmarks/writeToStream.js
  26. 187 0
      uni_modules/mqtt-packet/constants.js
  27. 52 0
      uni_modules/mqtt-packet/generate.js
  28. 3 0
      uni_modules/mqtt-packet/mqtt.js
  29. 58 0
      uni_modules/mqtt-packet/numbers.js
  30. 48 0
      uni_modules/mqtt-packet/package.json
  31. 13 0
      uni_modules/mqtt-packet/packet.js
  32. 716 0
      uni_modules/mqtt-packet/parser.js
  33. 2866 0
      uni_modules/mqtt-packet/test.js
  34. 86 0
      uni_modules/mqtt-packet/testRandom.js
  35. 255 0
      uni_modules/mqtt-packet/types/index.d.ts
  36. 1117 0
      uni_modules/mqtt-packet/writeToStream.js
  37. 27 0
      uni_modules/mqtt/CONTRIBUTING.md
  38. 15 0
      uni_modules/mqtt/LICENSE.md
  39. 689 0
      uni_modules/mqtt/README.md
  40. 146 0
      uni_modules/mqtt/bin/pub.js
  41. 123 0
      uni_modules/mqtt/bin/sub.js
  42. 14633 0
      uni_modules/mqtt/dist/mqtt.js
  43. 1 0
      uni_modules/mqtt/dist/mqtt.min.js
  44. 8 0
      uni_modules/mqtt/doc/help.txt
  45. 26 0
      uni_modules/mqtt/doc/publish.txt
  46. 26 0
      uni_modules/mqtt/doc/subscribe.txt
  47. 24 0
      uni_modules/mqtt/examples/client/secure-client.js
  48. 13 0
      uni_modules/mqtt/examples/client/simple-both.js
  49. 7 0
      uni_modules/mqtt/examples/client/simple-publish.js
  50. 9 0
      uni_modules/mqtt/examples/client/simple-subscribe.js
  51. 34 0
      uni_modules/mqtt/examples/tls client/crt.ca.cg.pem
  52. 48 0
      uni_modules/mqtt/examples/tls client/mqttclient.js
  53. 13 0
      uni_modules/mqtt/examples/tls client/tls-cert.pem
  54. 15 0
      uni_modules/mqtt/examples/tls client/tls-key.pem
  55. 49 0
      uni_modules/mqtt/examples/wss/client.js
  56. 58 0
      uni_modules/mqtt/examples/wss/client_with_proxy.js
  57. 1460 0
      uni_modules/mqtt/lib/client.js
  58. 130 0
      uni_modules/mqtt/lib/connect/ali.js
  59. 157 0
      uni_modules/mqtt/lib/connect/index.js
  60. 19 0
      uni_modules/mqtt/lib/connect/tcp.js
  61. 41 0
      uni_modules/mqtt/lib/connect/tls.js
  62. 92 0
      uni_modules/mqtt/lib/connect/ws.js
  63. 134 0
      uni_modules/mqtt/lib/connect/wx.js
  64. 140 0
      uni_modules/mqtt/lib/store.js
  65. 52 0
      uni_modules/mqtt/lib/validations.js
  66. 41 0
      uni_modules/mqtt/mqtt.js
  67. 112 0
      uni_modules/mqtt/package.json
  68. 3084 0
      uni_modules/mqtt/test/abstract_client.js
  69. 135 0
      uni_modules/mqtt/test/abstract_store.js
  70. 132 0
      uni_modules/mqtt/test/browser/server.js
  71. 92 0
      uni_modules/mqtt/test/browser/test.js
  72. 1129 0
      uni_modules/mqtt/test/client.js
  73. 16 0
      uni_modules/mqtt/test/helpers/private-csr.pem
  74. 27 0
      uni_modules/mqtt/test/helpers/private-key.pem
  75. 19 0
      uni_modules/mqtt/test/helpers/public-cert.pem
  76. 9 0
      uni_modules/mqtt/test/helpers/public-key.pem
  77. 52 0
      uni_modules/mqtt/test/helpers/server.js
  78. 9 0
      uni_modules/mqtt/test/helpers/server_process.js
  79. 14 0
      uni_modules/mqtt/test/helpers/tls-cert.pem
  80. 11 0
      uni_modules/mqtt/test/helpers/tls-csr.pem
  81. 15 0
      uni_modules/mqtt/test/helpers/tls-key.pem
  82. 13 0
      uni_modules/mqtt/test/helpers/wrong-cert.pem
  83. 11 0
      uni_modules/mqtt/test/helpers/wrong-csr.pem
  84. 15 0
      uni_modules/mqtt/test/helpers/wrong-key.pem
  85. 4 0
      uni_modules/mqtt/test/mocha.opts
  86. 230 0
      uni_modules/mqtt/test/mqtt.js
  87. 9 0
      uni_modules/mqtt/test/mqtt_store.js
  88. 157 0
      uni_modules/mqtt/test/secure_client.js
  89. 93 0
      uni_modules/mqtt/test/server.js
  90. 10 0
      uni_modules/mqtt/test/store.js
  91. 22 0
      uni_modules/mqtt/test/typescript/broker-connect-subscribe-and-publish.ts
  92. 14 0
      uni_modules/mqtt/test/typescript/tsconfig.json
  93. 13 0
      uni_modules/mqtt/test/util.js
  94. 144 0
      uni_modules/mqtt/test/websocket_client.js
  95. 27 0
      uni_modules/mqtt/types/index.d.ts
  96. 178 0
      uni_modules/mqtt/types/lib/client-options.d.ts
  97. 231 0
      uni_modules/mqtt/types/lib/client.d.ts
  98. 10 0
      uni_modules/mqtt/types/lib/connect/index.d.ts
  99. 6 0
      uni_modules/mqtt/types/lib/store-options.d.ts
  100. 0 0
      uni_modules/mqtt/types/lib/store.d.ts

+ 65 - 0
api/device/device.js

@@ -0,0 +1,65 @@
+import request from '@/utils/request'
+
+// 查询设备分组
+export function listDeviceGroup(userId) {
+    return request({
+        url: '/iot/group/list?pageSize=30&pageNum=1&userId='+userId,
+        method: 'get'
+    })
+}
+
+
+// 查询设备
+export function listDevice(pageNum,deviceName) {
+    let url = '/iot/device/shortList?pageNum='+pageNum+'&pageSize=12';
+    if(deviceName){
+        url  = url +"&deviceName="+encodeURIComponent(deviceName)
+    }
+    return request({
+        url: url,
+        method: 'get'
+    })
+}
+
+export function getDetail(id){
+    return request({
+        url: '/iot/device/'+id,
+        method: 'get'
+    })
+}
+
+export function getDeviceStatus(id,childId){
+    let url = '/iot/device/runningStatus/'+id;
+    if(childId){
+        url = url+"?childId="+childId;
+    }
+    return request({
+        url: url,
+        method: 'get'
+    })
+}
+
+export function cacheJsonThingsModel(id){
+    let url = '/iot/model/cache/'+id;
+    return request({
+        url: url,
+        method: 'get'
+    })
+}
+
+
+export function getDeviceMsg(){
+    let url = '/iot/configqrcode/list';
+    return request({
+        url: url,
+        method: 'get'
+    })
+}
+
+export function bindDeviceQrcode(qrcode,deviceNo){
+    let url = '/iot/device/bind?deviceNo='+deviceNo+"&qrcode="+qrcode;
+    return request({
+        url: url,
+        method: 'get',
+    })
+}

+ 4 - 3
config.js

@@ -1,7 +1,8 @@
 // 应用全局配置
-module.exports = {
-  baseUrl: 'https://vue.ruoyi.vip/prod-api',
-  // baseUrl: 'http://localhost:8080',
+module.exports = {
+  //baseUrl: 'https://vue.ruoyi.vip/prod-api',
+  baseUrl: 'https://yun.dnzc.vip',
+  //baseUrl: 'http://127.0.0.1:8991',
   // 应用信息
   appInfo: {
     // 应用名称

+ 6 - 2
main.js

@@ -3,15 +3,19 @@ import App from './App'
 import store from './store' // store
 import plugins from './plugins' // plugins
 import './permission' // permission
+import uView from '@/uni_modules/uview-ui'
+import mqttTool from '@/utils/mqttTool'
+
+Vue.use(uView)
 Vue.use(plugins)
 
 Vue.config.productionTip = false
 Vue.prototype.$store = store
-
+Vue.prototype.$mqttTool = mqttTool
 App.mpType = 'app'
 
 const app = new Vue({
   ...App
 })
 
-app.$mount()
+app.$mount()

+ 14 - 3
manifest.json

@@ -1,6 +1,6 @@
 {
     "name" : "若依移动端",
-    "appid" : "__UNI__25A9D80",
+    "appid" : "__UNI__E4C911E",
     "description" : "",
     "versionName" : "1.1.0",
     "versionCode" : "100",
@@ -41,7 +41,13 @@
     },
     "quickapp" : {},
     "mp-weixin" : {
-        "appid" : "wxccd7e2a0911b3397",
+        "appid" : "wx67fbe90ca1db0372",
+        "plugins" : {
+            "airkiss" : {
+                "version" : "1.1.0",
+                "provider" : "wx610ea582556c983e"
+            }
+        },
         "setting" : {
             "urlCheck" : false,
             "es6" : false,
@@ -51,7 +57,12 @@
         "optimization" : {
             "subPackages" : true
         },
-        "usingComponents" : true
+        "usingComponents" : true,
+        "permission" : {
+            "scope.userLocation" : {
+                "desc" : "需要配置网络获取WIFI接口"
+            }
+        }
     },
     "vueVersion" : "2",
     "h5" : {

+ 27 - 8
pages.json

@@ -65,18 +65,37 @@
     "style": {
       "navigationBarTitleText": "浏览文本"
     }
-  }],
+  },{
+    "path": "pages/device/index",
+    "style": {
+      "navigationBarTitleText": "设备列表"
+    }
+  }
+      ,{
+            "path" : "pages/device/detail",
+      "style": {
+        "navigationBarTitleText": "详情"
+      }
+
+        },{
+      "path": "pages/wifi/index",
+      "style": {
+        "navigationBarTitleText": "配网"
+      }
+    },{
+      "path": "pages/device/scan",
+      "style": {
+        "navigationBarTitleText": "绑定设备"
+      }
+    }
+
+  ],
   "tabBar": {
     "color": "#000000",
     "selectedColor": "#000000",
     "borderStyle": "white",
     "backgroundColor": "#ffffff",
-    "list": [{
-        "pagePath": "pages/index",
-        "iconPath": "static/images/tabbar/home.png",
-        "selectedIconPath": "static/images/tabbar/home_.png",
-        "text": "首页"
-      }, {
+    "list": [ {
         "pagePath": "pages/work/index",
         "iconPath": "static/images/tabbar/work.png",
         "selectedIconPath": "static/images/tabbar/work_.png",
@@ -94,4 +113,4 @@
     "navigationBarTitleText": "RuoYi",
     "navigationBarBackgroundColor": "#FFFFFF"
   }
-}
+}

+ 633 - 0
pages/device/detail.vue

@@ -0,0 +1,633 @@
+<template>
+  <view class="content">
+    <view class="header">
+  <u--form>
+    <u-form-item   style="border:0px"
+                   label="设备名称:"
+                   labelWidth="auto"
+                   :borderBottom="false"
+    >
+      <u--input disabled
+                placeholder="请选择分组"
+                border="none"
+                color="gray"
+                disabledColor="white"
+                v-model="deviceInfo.deviceName"
+      ></u--input>
+    </u-form-item>
+
+    <u-form-item   style="border:0px"
+                   label="设备编号:"
+                   labelWidth="auto"
+                   :borderBottom="false"
+                   ref="item1"
+    >
+      <u--input disabled
+                border="none"
+                color="gray"
+                disabledColor="white"
+                v-model="deviceInfo.serialNumber"
+      ></u--input>
+    </u-form-item>
+
+    <u-form-item   style="border:0px"
+                   label="二维码ID:"
+                   labelWidth="auto"
+                   :borderBottom="false"
+                   ref="item1"
+    >
+      <u--input disabled
+                border="none"
+                color="gray"
+                disabledColor="white"
+                v-model="deviceInfo.qrcodeId"
+      ></u--input>
+    </u-form-item>
+
+    <u-form-item   style="border:0px"
+                   label="激活时间:"
+                   labelWidth="auto"
+                   :borderBottom="false"
+                   ref="item1"
+    >
+      <u--input disabled
+                border="none"
+                color="gray"
+                disabledColor="white"
+                :value="deviceInfo.activeTime"
+      ></u--input>
+    </u-form-item>
+
+    <u-form-item   style="border:0px"
+                   label="设备信号:"
+                   labelWidth="auto"
+                   :borderBottom="false"
+    >
+      <u--input disabled
+                border="none"
+                color="gray"
+                disabledColor="white"
+                :value="deviceInfo.rssi"
+      ></u--input>
+    </u-form-item>
+
+    <u-form-item v-if="deviceInfo.networkAddress != null"   style="border:0px"
+                   label="设备网络:"
+                   labelWidth="auto"
+                   :borderBottom="false"
+                   ref="item1"
+    >
+      <u--input disabled
+                border="none"
+                color="gray"
+                disabledColor="white"
+                :value="deviceInfo.networkAddress+'('+deviceInfo.networkIp+')'"
+      ></u--input>
+    </u-form-item>
+
+    <u-form-item   style="border:0px"
+                   label="设备状态:"
+                   labelWidth="auto"
+                   :borderBottom="false"
+    >
+       <view style="width:90rpx">
+         <u-tag v-if="deviceInfo.status === 4" text="离线"  size="mini" type="info"></u-tag>
+         <u-tag v-if="deviceInfo.status === 3" text="在线"  size="mini" type="success"></u-tag>
+         <u-tag v-if="deviceInfo.status === 2" text="禁用"  size="mini" type="error"></u-tag>
+         <u-tag v-if="deviceInfo.status === 1" text="未激活"  size="mini" type="error"></u-tag>
+
+       </view>
+
+    </u-form-item>
+
+
+  </u--form>
+    </view>
+    <view class="text-area">
+      <u-tabs :list="summary" keyName="tabName" @change="getDeviceStatus"></u-tabs>
+
+      <view class="prop-container" style="background: white;padding:10px ">
+        <u-form>
+          <u--text text="属性控制" style="margin-top:10px"></u--text>
+        <u-form-item   style="border:0px;position: relative" v-for="item in inputProp"
+                       :label="item.name"
+                       labelWidth="auto"
+                       :borderBottom="false"
+        >
+          <u--input  placeholder="请输入字符串" v-if="item.type === 'string'" v-model="item.shadow">
+            <template slot="suffix">
+              <u-button v-if="deviceInfo.status==3"
+                        @tap="send(item)"
+                        text="发送"
+                        type="success"
+                        size="mini"
+              ></u-button>
+              <u-button v-if="deviceInfo.status!=3"
+                        @tap="send(item)"
+                        text="发送"
+                        type="info"
+                        size="mini"
+              ></u-button>
+            </template>
+          </u--input>
+
+          <u--input placeholder="请输入整数" v-if="item.type === 'integer'"  v-model="item.shadow">
+            <template slot="suffix">
+              <u-button v-if="deviceInfo.status==3"
+                        @tap="send(item)"
+                        text="发送"
+                        type="success"
+                        size="mini"
+              ></u-button>
+              <u-button v-if="deviceInfo.status!=3"
+                        @tap="send(item)"
+                        text="发送"
+                        type="info"
+                        size="mini"
+              ></u-button>
+            </template>
+          </u--input>
+
+          <u--input placeholder="请使用英文逗号分隔的字符串" v-if="item.type === 'array'"  v-model="item.shadow">
+            <template slot="suffix">
+              <u-button v-if="deviceInfo.status==3"
+                        @tap="send(item)"
+                        text="发送"
+                        type="success"
+                        size="mini"
+              ></u-button>
+              <u-button v-if="deviceInfo.status!=3"
+                        @tap="send(item)"
+                        text="发送"
+                        type="info"
+                        size="mini"
+              ></u-button>
+            </template>
+          </u--input>
+
+        <view v-if="item.type === 'enum'" style="position: relative" >
+
+          <view @click="chooseItemData(item)" style="width:100%" v-if="item.text != null && item.text.length>0">{{ item.text }}</view>
+
+          <view @click="chooseItemData(item)" style="width:100%" v-else>请选择</view>
+          <u-button v-if="deviceInfo.status==3" customStyle="position: absolute;top: 8rpx;right: 20rpx;width: 13%;"
+                    @tap="send(item)"
+                    text="发送"
+                    type="success"
+                    size="mini"
+          ></u-button>
+
+        </view>
+
+        </u-form-item>
+
+          <u-line></u-line>
+          <u--text text="属性监控" style="margin-top:10px"></u--text>
+
+          <view>
+
+            <u-form-item   style="border:0px" v-for="item in deviceInfo.readOnlyList"
+                           :label="item.name"
+                           labelWidth="auto"
+                           :borderBottom="false"
+            >
+              <u-input  placeholder="请输入字符串" disabled=""  :value="item.shadow+' '+item.unit">
+              </u-input>
+
+            </u-form-item>
+
+
+          </view>
+        </u-form>
+
+      </view>
+      <u-picker @cancel="show=null" :show="show!=null" :columns="columns" @confirm="confirmItemData" keyName="text"></u-picker>
+
+    </view>
+  </view>
+</template>
+
+<script>
+import { getDetail,getDeviceStatus,cacheJsonThingsModel } from '@/api/device/device.js'
+import UButton from "../../uni_modules/uview-ui/components/u-button/u-button";
+import UForm from "../../uni_modules/uview-ui/components/u--form/u--form";
+export default {
+  components: {UForm, UButton},
+  data(){
+      return {
+        show:null,
+        value:"",
+        deviceInfo:{},
+        id:0,
+        summary:[],
+        activeName:0,
+        childId:"",
+        oneToMul:false,
+        inputProp:[],
+        watchProp:[],
+        columns:[]
+      }
+  },
+  onLoad: function(opt) {
+    this.id = opt.id;
+   this.connectMqtt();
+    this.getDetail();
+  },
+  destroyed() {
+    // 取消订阅主题
+    this.mqttUnSubscribe(this.deviceInfo);
+  },
+  methods:{
+    confirmItemData(e){
+      let data = e.value[0];
+      this.show.text = data.text;
+      this.show.shadow=data.value;
+      this.show = null;
+      console.log(data);
+    },
+    send(item){
+      if(this.deviceInfo.status != 3){
+        uni.showToast({
+          title:"设备暂未上线",
+          icon:"error"
+        })
+        return;
+      }
+      if(!item.shadow){
+        uni.showToast({
+          title:"数据不能为空",
+          icon:"error"
+        })
+        return;
+      }
+      this.publishThingsModel(this.deviceInfo,item)
+    },
+    /** 更新设备状态 */
+    updateDeviceStatus(device) {
+
+    },
+    chooseItemData(data){
+      if(this.deviceInfo.status != 3){
+        uni.showToast({
+          title:"设备暂未上线",
+          icon:"error"
+        })
+        return;
+      }
+      this.columns = [data.enumList];
+      this.show =data;
+    },
+    getDetail(){
+      let self = this;
+      getDetail(this.id).then(res=>{
+        self.deviceInfo = res.data;
+        self.parseSummay(res.data.summary)
+        self.mqttSubscribe(res.data);
+        self.getDeviceStatus();
+      });
+    },
+    getDeviceStatus(e){
+      let self = this;
+      if(!e){
+          e = {index:0};
+      }
+      this.activeName = e.index;
+      this.childId = this.summary[this.activeName].id;
+      getDeviceStatus(this.id,this.childId).then(res=>{
+            let data =res.data;
+            this.deviceInfo = data;
+            self.parseStatusData(data)
+      });
+    },
+    parseStatusData(data){
+      let arr = [];
+      let arrayList = data.arrayList;
+      arr = arr.concat(arrayList);
+      let enumList = data.enumList;
+      arr = arr.concat(enumList);
+      let integerList = data.integerList;
+      arr = arr.concat(integerList);
+      let stringList = data.stringList;
+      arr = arr.concat(stringList);
+      this.inputProp = arr;
+
+      let readOnlyList = data.readOnlyList;
+      this.watchProp = readOnlyList;
+
+    },
+    parseSummay(summary){
+      let self = this;
+      if(!summary){
+        summary = "";
+      }
+      if(summary.length>0){
+        this.summary = JSON.parse(summary);
+        if(self.summary.length>0){
+          this.oneToMul = true;
+        }
+        for (let i = 0; i < self.summary.length; i++) {
+          self.summary[i].tabName = self.summary[i].id+"_"+self.summary[i].name
+        }
+        let childId = "";
+        if(this.oneToMul){
+          let info = self.summary[self.activeName];
+          childId = info.id;
+        }
+        this.childId = childId;
+      }else{
+        this.summary = [{tabName:"详情查看"}]
+      }
+    },
+    goDeviceDetail(id){
+      uni.navigateTo({
+        url: '/pages/device/detail/detail?id='+id
+      });
+    },
+    async connectMqtt() {
+      if (this.$mqttTool.client == null) {
+        await this.$mqttTool.connect(this.vuex_token);
+      }
+      this.mqttCallback();
+    },
+    /** Mqtt订阅主题 */
+    mqttSubscribe(device) {
+      // 订阅当前设备状态和实时监测
+      let topicStatus = '/' + device.productId + '/' + device.serialNumber + '/status/post';
+      let topicProperty = '/' + device.productId + '/' + device.serialNumber + '/property/post';
+      let topicFunction = '/' + device.productId + '/' + device.serialNumber + '/function/post';
+      let topics = [];
+      topics.push(topicStatus);
+      topics.push(topicProperty);
+      topics.push(topicFunction);
+      this.$mqttTool.subscribe(topics);
+    },
+    /** Mqtt取消订阅主题 */
+    mqttUnSubscribe(device) {
+      // 订阅当前设备状态和实时监测
+      let topicStatus = '/' + device.productId + '/' + device.serialNumber + '/status/post';
+      let topicProperty = '/' + device.productId + '/' + device.serialNumber + '/property/post';
+      let topicFunction = '/' + device.productId + '/' + device.serialNumber + '/function/post';
+      let topics = [];
+      topics.push(topicStatus);
+      topics.push(topicProperty);
+      topics.push(topicFunction);
+      console.log('取消订阅', topics);
+      this.$mqttTool.unsubscribe(topics);
+    },
+    /* Mqtt回调处理 */
+    mqttCallback() {
+      this.$mqttTool.client.on('message', (topic, message, buffer) => {
+        let topics = topic.split('/');
+        let productId = topics[1];
+        let deviceNum = topics[2];
+        console.log('接收到内容:'+message);
+        message = JSON.parse(message.toString());
+        if (topics[3] == 'status') {
+          console.log('接收到【设备状态-运行】主题:', topic);
+          console.log('接收到【设备状态-运行】内容:', message);
+          // 更新列表中设备的状态
+          if (this.deviceInfo.serialNumber == deviceNum) {
+            this.deviceInfo.status = message.status;
+            this.deviceInfo.isShadow = message.isShadow;
+            this.deviceInfo.rssi = message.rssi;
+            this.updateDeviceStatus(this.deviceInfo);
+          }
+        }
+        if (topics[3] == 'property' || topics[3] == 'function') {
+          console.log('接收到【物模型】主题:', topic);
+          console.log('接收到【物模型】内容:', message);
+          if(this.oneToMul){
+            let curTabId = this.summary[this.activeName].id;
+            let msg = [];
+            for (let i = 0; i < message.length; i++) {
+              let curMsg = message[i];
+              let id = curMsg.id;
+              let ids = id.split("_");
+              let value = curMsg.value;
+              if(ids.length == 2){
+                if(curTabId == ids[1]){
+                  msg.push({id:ids[0],value:value});
+                }
+              }
+            }
+            message = msg;
+          }
+          // 更新列表中设备的属性
+          if (this.deviceInfo.serialNumber == deviceNum) {
+            for (let j = 0; j < message.length; j++) {
+              let isComplete = false;
+              // 布尔类型
+              for (let k = 0; k < this.deviceInfo.boolList.length && !isComplete; k++) {
+                if (this.deviceInfo.boolList[k].id == message[j].id) {
+                  this.deviceInfo.boolList[k].shadow = message[j].value;
+                  isComplete = true;
+                  break;
+                }
+              }
+              // 枚举类型
+              for (let k = 0; k < this.deviceInfo.enumList.length && !isComplete; k++) {
+                if (this.deviceInfo.enumList[k].id == message[j].id) {
+                  this.deviceInfo.enumList[k].shadow = message[j].value;
+                  isComplete = true;
+                  break;
+                }
+              }
+              // 字符串类型
+              for (let k = 0; k < this.deviceInfo.stringList.length && !isComplete; k++) {
+                if (this.deviceInfo.stringList[k].id == message[j].id) {
+                  this.deviceInfo.stringList[k].shadow = message[j].value;
+                  isComplete = true;
+                  break;
+                }
+              }
+              // 数组类型
+              for (let k = 0; k < this.deviceInfo.arrayList.length && !isComplete; k++) {
+                if (this.deviceInfo.arrayList[k].id == message[j].id) {
+                  this.deviceInfo.arrayList[k].shadow = message[j].value;
+                  isComplete = true;
+                  break;
+                }
+              }
+              // 整数类型
+              for (let k = 0; k < this.deviceInfo.integerList.length && !isComplete; k++) {
+                if (this.deviceInfo.integerList[k].id == message[j].id) {
+                  this.deviceInfo.integerList[k].shadow = message[j].value;
+                  isComplete = true;
+                  break;
+                }
+              }
+              // 小数类型
+              for (let k = 0; k < this.deviceInfo.decimalList.length && !isComplete; k++) {
+                if (this.deviceInfo.decimalList[k].id == message[j].id) {
+                  this.deviceInfo.decimalList[k].shadow = message[j].value;
+                  isComplete = true;
+                  break;
+                }
+              }
+              // 监测数据
+              for (let k = 0; k < this.deviceInfo.readOnlyList.length && !isComplete; k++) {
+                if (this.deviceInfo.readOnlyList[k].id == message[j].id) {
+                  this.deviceInfo.readOnlyList[k].shadow = message[j].value;
+                  // 更新图表
+                  // for (let m = 0; m < this.monitorChart.length; m++) {
+                  //   if (message[j].id == this.monitorChart[m].data.id) {
+                  //     let data = [{
+                  //       value: message[j].value,
+                  //       name: this.monitorChart[m].data.name
+                  //     }];
+                  //     this.monitorChart[m].chart.setOption({
+                  //       series: [{
+                  //         data: data
+                  //       }]
+                  //     });
+                  //     break;
+                  //   }
+                  // }
+                  isComplete = true;
+                  break;
+                }
+              }
+            }
+          }
+        }
+      });
+    },
+    /** 发布物模型 类型(1=属性,2=功能) */
+    publishThingsModel(device, model) {
+      // 获取缓存的Json物模型
+      cacheJsonThingsModel(device.productId).then(response => {
+        let thingsModel = JSON.parse(response.data);
+        let type = 0;
+        for (let i = 0; i < thingsModel.functions.length; i++) {
+          if (model.id == thingsModel.functions[i].id) {
+            type = 2;
+            break;
+          }
+        }
+        if (type == 0) {
+          for (let i = 0; i < thingsModel.properties.length; i++) {
+            if (model.id == thingsModel.properties[i].id) {
+              type = 1;
+              break;
+            }
+          }
+        }
+        if (type != 0) {
+          this.mqttPublish(type, device, model);
+        }
+      });
+    },
+    notifyError(res){
+      uni.showToast({
+        title:res,
+        icon:"error"
+      })
+    },
+    notifySuccess(res){
+      uni.showToast({
+        title:res,
+        icon:"success"
+      })
+    },
+    /**
+     * Mqtt发布消息
+     * @type 类型(1=属性,2=功能,3=OTA升级,4=实时监测)
+     * @device 设备
+     * @model 物模型
+     * */
+    mqttPublish(type, device, model) {
+      let topic = "";
+      let message = ""
+      let oneToMul = false;
+      if(this.summary.length>0){
+        oneToMul = true;
+      }
+      let modelId = model.id;
+      if(oneToMul){
+        let info = this.summary[this.activeName];
+        let childId = info.id;
+        modelId = modelId+"_"+childId;
+      }
+      if (type == 1) {
+        if (device.status == 3) {
+          // 属性,在线模式
+          topic = "/" + device.productId + "/" + device.serialNumber + "/property-online/get";
+        } else if (device.isShadow) {
+          // 属性,离线模式
+          topic = "/" + device.productId + "/" + device.serialNumber + "/property-offline/post";
+        }
+        message = '[{"id":"' + modelId + '","value":"' + model.shadow + '"}]';
+      } else if (type == 2) {
+        if (device.status == 3) {
+          // 功能,在线模式
+          topic = "/" + device.productId + "/" + device.serialNumber + "/function-online/get";
+
+        } else if (device.isShadow) {
+          // 功能,离线模式
+          topic = "/" + device.productId + "/" + device.serialNumber + "/function-offline/post";
+        }
+        message = '[{"id":"' + modelId + '","value":"' + model.shadow + '"}]';
+      } else if (type == 3) {
+        // OTA升级
+        topic = "/" + device.productId + "/" + device.serialNumber + "/ota/get";
+        message = '{"version":' + this.firmware.version + ',"downloadUrl":"' + this.getDownloadUrl(this.firmware.filePath) + '"}';
+      } else {
+        return;
+      }
+      if (topic != "") {
+        // 发布
+        this.$mqttTool.publish(topic, message, model.name).then(res => {
+          this.notifySuccess(res);
+        }).catch(res => {
+          this.notifyError(res);
+        });
+      }
+    },
+
+  }
+}
+</script>
+
+<style>
+.header{
+  width: 100%;
+  background: white;
+  padding:0px 20rpx
+
+}
+.content {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+}
+
+.logo {
+  height: 200rpx;
+  width: 200rpx;
+  margin-top: 200rpx;
+  margin-left: auto;
+  margin-right: auto;
+  margin-bottom: 50rpx;
+}
+
+.text-area {
+  margin:10px;
+  padding-bottom: 10px;
+  justify-content: center;
+  width: 100%;
+}
+.grid-text {
+
+  font-size: 14px;
+  color: #909399;
+  padding: 10rpx 0 20rpx 0rpx;
+  /* #ifndef APP-PLUS */
+  box-sizing: border-box;
+  /* #endif */
+}
+.title {
+  font-size: 36rpx;
+  color: #8f8f94;
+}
+</style>

+ 313 - 0
pages/device/index.vue

@@ -0,0 +1,313 @@
+<template>
+  <view class="content">
+    <view class="header">
+      <u-search placeholder="请输入设备名称" :clearabled="true" @clear="searchDevice" @custom="searchDevice" v-model="search.deviceName" @search="searchDevice"></u-search>
+    </view>
+    <view class="text-area">
+      <u-grid
+          :border="true"
+          col="3"
+      >
+        <u-grid-item @click="goDeviceDetail(listItem)"
+            v-for="(listItem,listIndex) in deviceList"
+            :key="listIndex"
+        >
+          <u--image  :showLoading="true" src="/static/images/device/device.png" width="50px" height="50px"></u--image>
+
+          <text class="grid-text" style="text-align: center;font-size: 24rpx">{{ listItem.deviceName }}
+          </text>
+          <text>
+            {{listItem.serialNumber}}
+
+          </text>
+          <view style="margin:20rpx 0px">
+            <u-tag v-if="listItem.status === 4" text="离线"  size="mini" type="info"></u-tag>
+            <u-tag v-if="listItem.status === 3" text="在线"  size="mini" type="success"></u-tag>
+            <u-tag v-if="listItem.status === 2" text="禁用"  size="mini" type="error"></u-tag>
+            <u-tag v-if="listItem.status === 1" text="未激活"  size="mini" type="error"></u-tag>
+          </view>
+
+        </u-grid-item>
+      </u-grid>
+
+    </view>
+    <u-picker @cancel="show=false" :show="show" :columns="columns" @confirm="chooseGroup" keyName="label"></u-picker>
+  </view>
+</template>
+
+<script>
+import { listDeviceGroup,listDevice } from '@/api/device/device.js'
+export default {
+  data(){
+      return {
+        value:"",
+        show:false,
+        groupName:"",
+        columns: [
+        ],
+        deviceList:[],
+        page:1,
+        search:{
+          deviceName:""
+        }
+
+      }
+  },
+  onLoad: function() {
+    this.connectMqtt();
+  },
+  destroyed() {
+    // 取消订阅主题
+    this.mqttUnSubscribe(this.deviceList);
+  },
+  methods:{
+    goDeviceDetail(item){
+      uni.navigateTo({
+        url: '/pages/device/detail?id='+item.deviceId
+      });
+    },
+    searchDevice(){
+      console.log(this.search.deviceName)
+        this.mqttUnSubscribe(this.deviceList);
+        this.listDevice();
+    },
+    listDevice(){
+      let self = this;
+      listDevice(this.page,this.search.deviceName).then(res=>{
+            let rows = res.rows;
+            self.deviceList = rows;
+            self.mqttSubscribe(self.deviceList);
+      })
+    },
+    listDeviceGroup(){
+      let self = this;
+      let id = this.$store.state.user.id;
+      listDeviceGroup(id).then(res=>{
+        self.columns = [];
+        let groups = [];
+        let rows = res.rows;
+        for (let i = 0; i < rows.length; i++) {
+          let row = rows[i];
+          let id = row.groupId;
+          let name = row.groupName;
+          groups.push({
+            label:name,
+            id:id
+          })
+        }
+        self.columns.push(groups) ;
+      });
+
+    },
+    chooseGroup(e){
+      let data = e.value[0];
+      console.log(data.id+","+data.label);
+      this.groupName = data.label;
+      this.show = false
+    },
+    /* 连接Mqtt消息服务器 */
+    async connectMqtt() {
+      if (this.$mqttTool.client == null) {
+        await this.$mqttTool.connect();
+      }
+      this.mqttCallback();
+      this.listDevice();
+    },
+    /* Mqtt回调处理  */
+    mqttCallback() {
+      this.$mqttTool.client.on('message', (topic, message, buffer) => {
+        let topics = topic.split('/');
+        let productId = topics[1];
+        let deviceNum = topics[2];
+        message = JSON.parse(message.toString());
+        if (topics[3] == 'status') {
+          console.log('接收到【设备状态】主题:', topic);
+          console.log('接收到【设备状态】内容:', message);
+          // 更新列表中设备的状态
+          for (let i = 0; i < this.deviceList.length; i++) {
+            if (this.deviceList[i].serialNumber == deviceNum) {
+              this.deviceList[i].status = message.status;
+              this.deviceList[i].isShadow = message.isShadow;
+              this.deviceList[i].rssi = message.rssi;
+              return;
+            }
+          }
+        }
+        if (topics[3] == "monitor") {
+          console.log('接收到【设备状态】主题:', topic);
+          console.log('接收到【设备状态】内容:', message);
+          // 实时监测
+          this.chartLoading = false;
+          for (let k = 0; k < message.length; k++) {
+            let value = message[k].value;
+            let id = message[k].id;
+            let remark = message[k].remark;
+            // 数据加载到图表
+            for (let i = 0; i < this.dataList.length; i++) {
+              if (id == this.dataList[i].id) {
+                if (this.dataList[i].length > 50) {
+                  this.dataList[i].shift();
+                }
+                this.dataList[i].data.push([this.getTime(), value]);
+                // 更新图表
+                this.chart[i].setOption({
+                  series: [{
+                    data: this.dataList[i].data
+                  }]
+                });
+                break;
+              }
+            }
+          }
+        }
+        if (topics[3] == 'property' || topics[3] == 'function') {
+          console.log('接收到【物模型】主题:', topic);
+          console.log('接收到【物模型】内容:', message);
+          // 更新列表中设备的属性
+          for (let i = 0; i < this.deviceList.length; i++) {
+            if (this.deviceList[i].serialNumber == deviceNum) {
+              for (let j = 0; j < message.length; j++) {
+                let isComplete = false;
+                // 布尔类型
+                for (let k = 0; k < this.deviceList[i].boolList.length && !isComplete; k++) {
+                  if (this.deviceList[i].boolList[k].id == message[j].id) {
+                    this.deviceList[i].boolList[k].shadow = message[j].value;
+                    isComplete = true;
+                    break;
+                  }
+                }
+                // 枚举类型
+                for (let k = 0; k < this.deviceList[i].enumList.length && !isComplete; k++) {
+                  if (this.deviceList[i].enumList[k].id == message[j].id) {
+                    this.deviceList[i].enumList[k].shadow = message[j].value;
+                    isComplete = true;
+                    break;
+                  }
+                }
+                // 字符串类型
+                for (let k = 0; k < this.deviceList[i].stringList.length && !isComplete; k++) {
+                  if (this.deviceList[i].stringList[k].id == message[j].id) {
+                    this.deviceList[i].stringList[k].shadow = message[j].value;
+                    isComplete = true;
+                    break;
+                  }
+                }
+                // 数组类型
+                for (let k = 0; k < this.deviceList[i].arrayList.length && !isComplete; k++) {
+                  if (this.deviceList[i].arrayList[k].id == message[j].id) {
+                    this.deviceList[i].arrayList[k].shadow = message[j].value;
+                    isComplete = true;
+                    break;
+                  }
+                }
+                // 整数类型
+                for (let k = 0; k < this.deviceList[i].integerList.length && !isComplete; k++) {
+                  if (this.deviceList[i].integerList[k].id == message[j].id) {
+                    this.deviceList[i].integerList[k].shadow = message[j].value;
+                    isComplete = true;
+                    break;
+                  }
+                }
+                // 小数类型
+                for (let k = 0; k < this.deviceList[i].decimalList.length && !isComplete; k++) {
+                  if (this.deviceList[i].decimalList[k].id == message[j].id) {
+                    this.deviceList[i].decimalList[k].shadow = message[j].value;
+                    isComplete = true;
+                    break;
+                  }
+                }
+                // 监测数据
+                for (let k = 0; k < this.deviceList[i].readOnlyList.length && !isComplete; k++) {
+                  if (this.deviceList[i].readOnlyList[k].id == message[j].id) {
+                    this.deviceList[i].readOnlyList[k].shadow = message[j].value;
+                    isComplete = true;
+                    break;
+                  }
+                }
+              }
+              return;
+            }
+          }
+        }
+      });
+    },
+    /* 订阅消息 */
+    mqttSubscribe(list) {
+      // 订阅当前页面设备状态和实时监测
+      let topics = [];
+      for (let i = 0; i < list.length; i++) {
+        let topicStatus = '/' + list[i].productId + '/' + list[i].serialNumber + '/status/post';
+        let topicMonitor = "/" + list[i].productId + "/" + list[i].serialNumber + "/monitor/post";
+        let topicProperty = '/' + list[i].productId + '/' + list[i].serialNumber + '/property/post';
+        let topicFunction = '/' + list[i].productId + '/' + list[i].serialNumber + '/function/post';
+        topics.push(topicStatus);
+        topics.push(topicMonitor);
+        topics.push(topicProperty);
+        topics.push(topicFunction);
+      }
+      this.$mqttTool.subscribe(topics);
+    },
+    mqttUnSubscribe(list){
+      let topics = [];
+      for (let i = 0; i < list.length; i++) {
+        let topicStatus = '/' + list[i].productId + '/' + list[i].serialNumber + '/status/post';
+        let topicMonitor = "/" + list[i].productId + "/" + list[i].serialNumber + "/monitor/post";
+        let topicProperty = '/' + list[i].productId + '/' + list[i].serialNumber + '/property/post';
+        let topicFunction = '/' + list[i].productId + '/' + list[i].serialNumber + '/function/post';
+        topics.push(topicStatus);
+        topics.push(topicMonitor);
+        topics.push(topicProperty);
+        topics.push(topicFunction);
+      }
+      console.log('取消订阅', topics);
+      this.$mqttTool.unsubscribe(topics);
+    }
+
+  }
+}
+</script>
+
+<style>
+.header{
+  height: 100rpx;
+  width: 100%;
+  background: white;
+  padding:0px 20rpx
+
+}
+.content {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+}
+
+.logo {
+  height: 200rpx;
+  width: 200rpx;
+  margin-top: 200rpx;
+  margin-left: auto;
+  margin-right: auto;
+  margin-bottom: 50rpx;
+}
+
+.text-area {
+  margin:10px;
+  padding-bottom: 10px;
+  justify-content: center;
+  width: 100%;
+}
+.grid-text {
+
+  font-size: 14px;
+  color: #909399;
+  padding: 10rpx 0 20rpx 0rpx;
+  /* #ifndef APP-PLUS */
+  box-sizing: border-box;
+  /* #endif */
+}
+.title {
+  font-size: 36rpx;
+  color: #8f8f94;
+}
+</style>

+ 162 - 0
pages/device/scan.vue

@@ -0,0 +1,162 @@
+<template>
+  <view class="help-container" >
+    <view style="font-size: 12px;padding-bottom: 10rpx">
+
+      TIPS:请通过设备发送指令后,然后在该列表中绑定相应设备。
+    </view>
+    <view style="background: white;padding-top:00px;position: absolute;top:108rpx;bottom: 10px;left:10px;right:10px;text-align: center">
+    <u-list height="100%"
+    >
+      <view style="position: relative;text-align: center;padding:30rpx">
+          消息列表
+         <view @click="getList" style="position: absolute;    right: 50rpx;top: 20rpx;">
+           <u-icon name="reload" ></u-icon>
+         </view>
+      </view>
+      <u-grid col="5"
+              :border="false"
+              @click="click"
+      >
+        <u-grid-item
+            v-for="(baseListItem,baseListIndex) in headTitle"
+            :key="baseListIndex"
+        >
+
+          <text class="grid-text">{{baseListItem}}</text>
+        </u-grid-item>
+      </u-grid>
+      <u-list-item
+          v-for="(item, index) in indexList"
+          :key="index"
+      >
+      <view style="padding-top: 10px;text-align: center;height: 100rpx;font-size:10px;;border-bottom:1px solid lightgray">
+        <u-grid col="5"
+                :border="false"
+        >
+          <u-grid-item
+          >
+
+            <text class="grid-text"> {{item.deviceNo}} </text>
+          </u-grid-item>
+          <u-grid-item
+          >
+
+            <text class="grid-text">  {{item.deviceName}}</text>
+          </u-grid-item>
+          <u-grid-item
+          >
+
+            <text class="grid-text"> {{item.productName}}</text>
+          </u-grid-item>
+          <u-grid-item
+          >
+
+            <text class="grid-text"> {{item.createTime}}</text>
+          </u-grid-item>
+          <u-grid-item
+          >
+
+            <u-icon @click="scanCode(item.deviceNo)" name="scan" color="#2979ff" size="28" ></u-icon>
+
+          </u-grid-item>
+        </u-grid>
+
+
+      </view>
+      </u-list-item>
+    </u-list>
+    </view>
+  </view>
+</template>
+
+<script>
+import { getDeviceMsg,bindDeviceQrcode } from '@/api/device/device.js'
+  export default {
+    data() {
+      return {
+        headTitle:["设备编号","设备名称","产品编号","消息时间","操作"],
+        indexList: [
+        ]
+      }
+    },
+    onShow(){
+      this.getList();
+    },
+    methods: {
+      scanCode(deviceNo){
+        uni.scanCode({
+          success: function (res) {
+            console.log('条码内容:' + res.result);
+            bindDeviceQrcode(res.result,deviceNo).then(res=>{
+              let code = res.code
+              if(code == '200'){
+                uni.showToast({title:res.msg})
+              }else{
+                uni.showToast({title:res.msg,icon:"error"})
+              }
+
+            });
+          }
+        });
+      },
+      getList(){
+        getDeviceMsg().then(res=>{
+          this.indexList = res.rows;
+        });
+      },
+      handleText(item) {
+        this.$tab.navigateTo(`/pages/common/textview/index?title=${item.title}&content=${item.content}`)
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  page {
+    background-color: #f8f8f8;
+  }
+
+  .help-container {
+    margin-bottom: 100rpx;
+    padding: 30rpx;
+  }
+
+  .list-title {
+    margin-bottom: 30rpx;
+  }
+
+  .childList {
+    background: #ffffff;
+    box-shadow: 0px 0px 10rpx rgba(193, 193, 193, 0.2);
+    border-radius: 16rpx;
+    margin-top: 10rpx;
+  }
+
+  .line {
+    width: 100%;
+    height: 1rpx;
+    background-color: #F5F5F5;
+  }
+
+  .text-title {
+    color: #303133;
+    font-size: 32rpx;
+    font-weight: bold;
+    margin-left: 10rpx;
+
+    .iconfont {
+      font-size: 16px;
+      margin-right: 10rpx;
+    }
+  }
+
+  .text-item {
+    font-size: 28rpx;
+    padding: 24rpx;
+  }
+
+  .question {
+    color: #606266;
+    font-size: 28rpx;
+  }
+</style>

+ 32 - 23
pages/login.vue

@@ -1,9 +1,9 @@
 <template>
   <view class="normal-login-container">
     <view class="logo-content align-center justify-center flex">
-      <image style="width: 100rpx;height: 100rpx;" :src="globalConfig.appInfo.logo" mode="widthFix">
-      </image>
-      <text class="title">若依移动端登录</text>
+<!--      <image style="width: 100rpx;height: 100rpx;" :src="globalConfig.appInfo.logo" mode="widthFix">-->
+<!--      </image>-->
+      <text class="title">天目云平台移动端登录</text>
     </view>
     <view class="login-form-content">
       <view class="input-item flex align-center">
@@ -17,8 +17,8 @@
       <view class="input-item flex align-center" style="width: 60%;margin: 0px;" v-if="captchaEnabled">
         <view class="iconfont icon-code icon"></view>
         <input v-model="loginForm.code" type="number" class="input" placeholder="请输入验证码" maxlength="4" />
-        <view class="login-code"> 
-          <image :src="codeUrl" @click="getCode" class="login-code-img"></image>
+        <view class="login-code">
+          <image :src="codeUrl" @click="getCode" class="login-code-img"></image>
         </view  >
       </view>
       <view class="action-btn">
@@ -27,15 +27,16 @@
     </view>
 
     <view class="xieyi text-center">
-      <text class="text-grey1">登录即代表同意</text>
-      <text @click="handleUserAgrement" class="text-blue">《用户协议》</text>
-      <text @click="handlePrivacy" class="text-blue">《隐私协议》</text>
+<!--      <text class="text-grey1">登录即代表同意</text>-->
+<!--      <text @click="handleUserAgrement" class="text-blue">《用户协议》</text>-->
+<!--      <text @click="handlePrivacy" class="text-blue">《隐私协议》</text>-->
     </view>
   </view>
 </template>
 
 <script>
   import { getCodeImg } from '@/api/login'
+  import {getToken} from "@/utils/auth";
 
   export default {
     data() {
@@ -52,9 +53,17 @@
       }
     },
     created() {
-      this.getCode()
+      this.checkLogin();
     },
     methods: {
+      checkLogin(){
+        let token = getToken()
+        if(token){
+          this.loginSuccess()
+        }else{
+          this.getCode()
+        }
+      },
       // 隐私协议
       handlePrivacy() {
         let site = this.globalConfig.appInfo.agreements[0]
@@ -103,7 +112,7 @@
       loginSuccess(result) {
         // 设置用户信息
         this.$store.dispatch('GetInfo').then(res => {
-          this.$tab.reLaunch('/pages/index')
+          this.$tab.reLaunch('/pages/work/index')
         })
       }
     }
@@ -169,20 +178,20 @@
       .xieyi {
         color: #333;
         margin-top: 20px;
-      }
-      
-      .login-code {
-        height: 38px;
-        float: right;
-      
-        .login-code-img {
-          height: 38px;
-          position: absolute;
-          margin-left: 10px;
-          width: 200rpx;
-        }
+      }
+
+      .login-code {
+        height: 38px;
+        float: right;
+
+        .login-code-img {
+          height: 38px;
+          position: absolute;
+          margin-left: 10px;
+          width: 200rpx;
+        }
       }
     }
   }
-
+
 </style>

+ 11 - 11
pages/mine/about/index.vue

@@ -3,7 +3,7 @@
     <view class="header-section text-center">
       <image style="width: 150rpx;height: 150rpx;" src="/static/logo200.png" mode="widthFix">
       </image>
-      <uni-title type="h2" title="若依移动端"></uni-title>
+      <uni-title type="h2" title="云平台移动端"></uni-title>
     </view>
 
     <view class="content-section">
@@ -43,15 +43,15 @@
   </view>
 </template>
 
-<script>
-  export default {
-    data() {
-      return {
-        url: getApp().globalData.config.appInfo.site_url,
-        version: getApp().globalData.config.appInfo.version
-      }
-    }
-  }
+<script>
+  export default {
+    data() {
+      return {
+        url: getApp().globalData.config.appInfo.site_url,
+        version: getApp().globalData.config.appInfo.version
+      }
+    }
+  }
 </script>
 
 <style lang="scss">
@@ -72,4 +72,4 @@
     flex-direction: column;
     align-items: center;
   }
-</style>
+</style>

+ 10 - 10
pages/mine/index.vue

@@ -25,8 +25,8 @@
       </view>
     </view>
 
-    <view class="content-section">
-      <view class="mine-actions grid col-4 text-center">
+    <view class="content-section" style="margin-top:122rpx">
+      <view class="mine-actions grid col-4 text-center" style="display: none">
         <view class="action-item" @click="handleJiaoLiuQun">
           <view class="iconfont icon-friendfill text-pink icon"></view>
           <text class="text">交流群</text>
@@ -45,19 +45,19 @@
         </view>
       </view>
 
-      <view class="menu-list">
+      <view class="menu-list" >
         <view class="list-cell list-cell-arrow" @click="handleToEditInfo">
           <view class="menu-item-box">
             <view class="iconfont icon-user menu-icon"></view>
             <view>编辑资料</view>
           </view>
         </view>
-        <view class="list-cell list-cell-arrow" @click="handleHelp">
-          <view class="menu-item-box">
-            <view class="iconfont icon-help menu-icon"></view>
-            <view>常见问题</view>
-          </view>
-        </view>
+<!--        <view class="list-cell list-cell-arrow" @click="handleHelp">-->
+<!--          <view class="menu-item-box">-->
+<!--            <view class="iconfont icon-help menu-icon"></view>-->
+<!--            <view>常见问题</view>-->
+<!--          </view>-->
+<!--        </view>-->
         <view class="list-cell list-cell-arrow" @click="handleAbout">
           <view class="menu-item-box">
             <view class="iconfont icon-aixin menu-icon"></view>
@@ -78,7 +78,7 @@
 
 <script>
   import storage from '@/utils/storage'
-  
+
   export default {
     data() {
       return {

+ 241 - 0
pages/wifi/index.vue

@@ -0,0 +1,241 @@
+<template>
+  <view class="content"style="background: white">
+    <view class="text-area">
+      <u--form style="width: 100%;"
+               labelPosition="left"
+               ref="uForm"
+      >
+        <u-form-item
+            label="WIFI名称"
+            borderBottom
+            labelWidth="auto"
+            @click="openChooseWifi"
+            ref="item1"
+        >
+          <u--input
+              v-model="SSID"
+              disabled
+              disabledColor="#ffffff"
+              placeholder="请选择wifi"
+              border="none"
+          ></u--input>
+          <u-icon
+              slot="right"
+              name="arrow-right"
+          ></u-icon>
+        </u-form-item>
+        <u-form-item
+            label="密码"
+            borderBottom
+            ref="item1"
+        >
+          <u--input
+              v-model="password"
+              border="none"
+          ></u--input>
+        </u-form-item>
+
+      </u--form>
+
+    </view>
+    <view style="margin:10px">
+      <u-button text="开始配网" @click="doConnect" size="small" type="success"></u-button>
+
+    </view>
+    <u-picker @cancel="showWiftList=false" @confirm="chooseWifi"  :show="showWiftList" :columns="wifiList"></u-picker>
+  </view>
+</template>
+
+<script>
+  export default {
+    data(){
+
+      return{
+        showWiftList:false,
+        wifiList: [
+          []
+        ],
+        SSID:"",
+        password:""
+      }
+    },
+    onLoad: function() {
+      this.openWifi();
+    },
+    methods:{
+		getAuth(){
+			wx.getSetting({
+			  success(res) {
+			    if (!res.authSetting['scope.userLocation']) {
+			      wx.authorize({
+			        scope: 'scope.userLocation',
+			        success () {
+			          // 用户已经同意小程序使用录音功能,后续调用 wx.startRecord 接口不会弹窗询问
+
+			        }
+			      })
+			    }
+			  }
+			})
+		},
+      chooseWifi(e){
+        this.SSID = e.value[0];
+        this.showWiftList= false;
+      },
+      openChooseWifi(){
+        if(this.wifiList[0].length == 0){
+            this.getWifiList();
+        }else{
+          this.showWiftList = true;
+        }
+      },
+      openWifi(){
+        let self = this;
+        // #ifdef MP-WEIXIN
+        wx.startWifi({
+          success(res) {
+            console.log(res);
+            // self.getWifiList();
+          },
+          fail(res) {
+            console.log(res)
+            uni.showToast({
+              title: '请打开WIFI',
+              icon: 'none',
+              duration: 3000
+            });
+
+          },
+        })
+        // #endif
+
+      },
+      getWifi(){
+        // #ifdef MP-WEIXIN
+        var that = this
+        wx.getConnectedWifi({
+          success(res) {
+            console.log(res)
+            that.BSSID = res.wifi.BSSID
+            that.WIFIName = res.wifi.SSID
+          },
+          fail(res) {
+            console.log(res)
+            //报错的相关处理
+          },
+        })
+        // #endif
+
+      },
+      getWifiList(){
+        // #ifdef MP-WEIXIN
+        var that = this
+        uni.showLoading();
+        wx.getWifiList({
+          success(res) {
+            console.log(res)
+            wx.onGetWifiList(function(res) {
+              that.showWiftList = true;
+              uni.hideLoading();
+              console.log("获取wifi列表");
+              that.wifiList = [[]];
+              console.log(res.wifiList); //在这里提取列表数据
+              //通过遍历将WIFI名字存入集合,以便下卡框等组件使用
+              for (var i = 0; i < res.wifiList.length; i++) {
+                that.wifiList[0].push(res.wifiList[i].SSID)
+              }
+            })
+          },
+          fail(res) {
+            console.log(res)
+            uni.showToast({
+              title: '获取wifi失败,请检查wifi',
+              icon: 'none',
+              duration: 2000
+            });
+          },
+        })
+        // #endif
+
+      },
+      doConnect(){
+        // #ifdef MP-WEIXIN
+        const airkiss = requirePlugin('airkiss');
+        if (this.SSID != '' && this.password != '') {
+          console.log(airkiss)
+          uni.showLoading({
+            title: '配网中请稍后..'
+          });
+          airkiss.startAirkiss(this.SSID, this.password, function(res) {
+            console.log(res)
+            switch (res.code) {
+              case 0:
+                uni.hideLoading();
+                uni.showModal({
+                  title: '初始化失败',
+                  content: res.result,
+                  showCancel: false,
+                  confirmText: '收到',
+                })
+                break;
+              case 1:
+                uni.hideLoading();
+                uni.showModal({
+                  title: '配网成功',
+                  content: '设备IP:' + res.ip + '\r\n 设备Mac:' + res.bssid,
+                  showCancel: false,
+                  confirmText: '好的',
+                })
+                break;
+              case 2:
+                uni.hideLoading();
+                uni.showModal({
+                  title: '配网失败',
+                  content: '请检查密码是否正确',
+                  showCancel: false,
+                  confirmText: '收到',
+                })
+                break;
+
+              default:
+                uni.hideLoading();
+                break;
+            }
+
+          })
+        } else {
+          uni.showToast({
+            title: '请选择WIFI并输入密码',
+            icon: 'none',
+            duration: 3000
+          });
+        }
+        // #endif
+
+      }
+    }
+  }
+</script>
+
+<style>
+  .content {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+  }
+
+
+  .text-area {
+    display: flex;
+    justify-content: center;
+    background: white;
+    width: 100%;
+    height: 100%;
+  }
+
+  .title {
+    font-size: 36rpx;
+    color: #8f8f94;
+  }
+</style>

+ 35 - 53
pages/work/index.vue

@@ -12,61 +12,26 @@
     </uni-swiper-dot>
 
     <!-- 宫格组件 -->
-    <uni-section title="系统管理" type="line"></uni-section>
+    <uni-section title="工作台" type="line"></uni-section>
     <view class="grid-body">
-      <uni-grid :column="4" :showBorder="false" @change="changeGrid">
+      <uni-grid :column="4" :showBorder="false" >
         <uni-grid-item>
-          <view class="grid-item-box">
-            <uni-icons type="person-filled" size="30"></uni-icons>
-            <text class="text">用户管理</text>
+          <view class="grid-item-box" @click="goPage('device')">
+            <uni-icons type="cloud-upload" color="#2979ff" size="30"></uni-icons>
+            <text class="text">设备查看</text>
           </view>
         </uni-grid-item>
         <uni-grid-item>
-          <view class="grid-item-box">
-            <uni-icons type="staff-filled" size="30"></uni-icons>
-            <text class="text">角色管理</text>
-          </view>
-        </uni-grid-item>
-        <uni-grid-item>
-          <view class="grid-item-box">
-            <uni-icons type="color" size="30"></uni-icons>
-            <text class="text">菜单管理</text>
-          </view>
-        </uni-grid-item>
-        <uni-grid-item>
-          <view class="grid-item-box">
-            <uni-icons type="settings-filled" size="30"></uni-icons>
-            <text class="text">部门管理</text>
-          </view>
-        </uni-grid-item>
-        <uni-grid-item>
-          <view class="grid-item-box">
-            <uni-icons type="heart-filled" size="30"></uni-icons>
-            <text class="text">岗位管理</text>
-          </view>
-        </uni-grid-item>
-        <uni-grid-item>
-          <view class="grid-item-box">
-            <uni-icons type="bars" size="30"></uni-icons>
-            <text class="text">字典管理</text>
-          </view>
-        </uni-grid-item>
-        <uni-grid-item>
-          <view class="grid-item-box">
-            <uni-icons type="gear-filled" size="30"></uni-icons>
-            <text class="text">参数设置</text>
-          </view>
-        </uni-grid-item>
-        <uni-grid-item>
-          <view class="grid-item-box">
-            <uni-icons type="chat-filled" size="30"></uni-icons>
-            <text class="text">通知公告</text>
+          <view class="grid-item-box" @click="goPage('wifi')">
+            <u-icon name="wifi" color="#2979ff" size="28"></u-icon>
+            <text class="text">设备配网</text>
           </view>
         </uni-grid-item>
+
         <uni-grid-item>
-          <view class="grid-item-box">
-            <uni-icons type="wallet-filled" size="30"></uni-icons>
-            <text class="text">日志管理</text>
+          <view class="grid-item-box" @click="goPage('scan')">
+            <u-icon name="scan" color="#2979ff" size="28"></u-icon>
+            <text class="text">扫码绑定</text>
           </view>
         </uni-grid-item>
       </uni-grid>
@@ -83,12 +48,12 @@
         data: [{
             image: '/static/images/banner/banner01.jpg'
           },
-          {
-            image: '/static/images/banner/banner02.jpg'
-          },
-          {
-            image: '/static/images/banner/banner03.jpg'
-          }
+          // {
+          //   image: '/static/images/banner/banner02.jpg'
+          // },
+          // {
+          //   image: '/static/images/banner/banner03.jpg'
+          // }
         ]
       }
     },
@@ -101,6 +66,23 @@
       },
       changeGrid(e) {
         this.$modal.showToast('模块建设中~')
+      },
+      goPage(path){
+        if('device' == path){
+          uni.navigateTo({
+            url: '/pages/device/index'
+          });
+        }
+        if('wifi' == path){
+          uni.navigateTo({
+            url: '/pages/wifi/index'
+          });
+        }
+        if('scan' == path){
+          uni.navigateTo({
+            url: '/pages/device/scan'
+          });
+        }
       }
     }
   }

BIN
static/images/banner/banner01.jpg


BIN
static/images/device/device.png


+ 9 - 2
store/modules/user.js

@@ -12,7 +12,8 @@ const user = {
     name: storage.get(constant.name),
     avatar: storage.get(constant.avatar),
     roles: storage.get(constant.roles),
-    permissions: storage.get(constant.permissions)
+    permissions: storage.get(constant.permissions),
+    id:storage.get(constant.id)
   },
 
   mutations: {
@@ -34,6 +35,10 @@ const user = {
     SET_PERMISSIONS: (state, permissions) => {
       state.permissions = permissions
       storage.set(constant.permissions, permissions)
+    },
+    SET_ID: (state, id) => {
+      state.id = id
+      storage.set(constant.id, id)
     }
   },
 
@@ -60,6 +65,7 @@ const user = {
       return new Promise((resolve, reject) => {
         getInfo().then(res => {
           const user = res.user
+          const userId = user.userId
           const avatar = (user == null || user.avatar == "" || user.avatar == null) ? require("@/static/images/profile.jpg") : baseUrl + user.avatar
           const username = (user == null || user.userName == "" || user.userName == null) ? "" : user.userName
           if (res.roles && res.roles.length > 0) {
@@ -68,6 +74,7 @@ const user = {
           } else {
             commit('SET_ROLES', ['ROLE_DEFAULT'])
           }
+          commit('SET_ID', userId)
           commit('SET_NAME', username)
           commit('SET_AVATAR', avatar)
           resolve(res)
@@ -95,4 +102,4 @@ const user = {
   }
 }
 
-export default user
+export default user

+ 3 - 1
uni.scss

@@ -1,6 +1,8 @@
 /**
  * uni-app内置的常用样式变量
  */
+@import "@/uni_modules/uview-ui/theme.scss";
+@import "@/uni_modules/uview-ui/index.scss";
 
 /* 行为相关颜色 */
 $uni-color-primary: #007aff;
@@ -61,4 +63,4 @@ $uni-font-size-title:20px;
 $uni-color-subtitle: #555555; // 二级标题颜色
 $uni-font-size-subtitle:26px;
 $uni-color-paragraph: #3F536E; // 文章段落颜色
-$uni-font-size-paragraph:15px;
+$uni-font-size-paragraph:15px;

+ 27 - 0
uni_modules/mqtt-packet/.github/workflows/ci.yml

@@ -0,0 +1,27 @@
+name: ci
+
+on: [push, pull_request]
+
+jobs:
+  test:
+    runs-on: ubuntu-latest
+
+    strategy:
+      matrix:
+        node-version: [6.x, 8.x, 10.x, 12.x, 14.x]
+
+    steps:
+      - uses: actions/checkout@v1
+
+      - name: Use Node.js
+        uses: actions/setup-node@v1
+        with:
+          node-version: ${{ matrix.node-version }}
+
+      - name: Install
+        run: |
+          npm install
+
+      - name: Run tests
+        run: |
+          npm run ci

+ 27 - 0
uni_modules/mqtt-packet/CONTRIBUTING.md

@@ -0,0 +1,27 @@
+# mqtt-packet is an OPEN Open Source Project
+
+-----------------------------------------
+
+## What?
+
+Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project.
+
+## Rules
+
+There are a few basic ground-rules for contributors:
+
+1. **No `--force` pushes** or modifying the Git history in any way.
+1. **Non-master branches** ought to be used for ongoing work.
+1. **External API changes and significant modifications** ought to be subject to an **internal pull-request** to solicit feedback from other contributors.
+1. Internal pull-requests to solicit feedback are *encouraged* for any other non-trivial contribution but left to the discretion of the contributor.
+1. Contributors should attempt to adhere to the prevailing code-style.
+
+## Releases
+
+Declaring formal releases remains the prerogative of the project maintainer.
+
+## Changes to this arrangement
+
+This is an experiment and feedback is welcome! This document may also be subject to pull-requests or changes by contributors where you believe you have something valuable to add or change.
+
+-----------------------------------------

+ 13 - 0
uni_modules/mqtt-packet/LICENSE.md

@@ -0,0 +1,13 @@
+The MIT License (MIT)
+=====================
+
+Copyright (c) 2014-2017 mqtt-packet contributors
+---------------------------------------
+
+*mqtt-packet contributors listed at <https://github.com/mqttjs/mqtt-packet#contributors>*
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 491 - 0
uni_modules/mqtt-packet/README.md

@@ -0,0 +1,491 @@
+mqtt-packet
+===========
+
+Encode and Decode MQTT 3.1.1, 5.0 packets the node way.
+
+[![JavaScript Style Guide](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard)
+
+  * <a href="#installation">Installation</a>
+  * <a href="#examples">Examples</a>
+  * <a href="#packets">Packets</a>
+  * <a href="#api">API</a>
+  * <a href="#contributing">Contributing</a>
+  * <a href="#license">License &amp; copyright</a>
+
+This library is tested with node v6, v8, v10, v12 and v14. The last version to support
+older versions of node was mqtt-packet@4.1.2.
+
+Installation
+------------
+
+```bash
+npm install mqtt-packet --save
+```
+
+Examples
+--------
+
+### Generating
+
+```js
+const mqtt = require('mqtt-packet');
+const object = {
+  cmd: 'publish',
+  retain: false,
+  qos: 0,
+  dup: false,
+  length: 10,
+  topic: 'test',
+  payload: 'test' // Can also be a Buffer
+};
+const opts = { protocolVersion: 4 }; // default is 4. Usually, opts is a connect packet
+
+console.log(mqtt.generate(object))
+// Prints:
+//
+// <Buffer 30 0a 00 04 74 65 73 74 74 65 73 74>
+//
+// Which is the same as:
+//
+// Buffer.from([
+//   48, 10, // Header (publish)
+//   0, 4, // Topic length
+//   116, 101, 115, 116, // Topic (test)
+//   116, 101, 115, 116 // Payload (test)
+// ])
+```
+
+### Parsing
+
+```js
+const mqtt = require('mqtt-packet');
+const opts = { protocolVersion: 4 }; // default is 4. Usually, opts is a connect packet
+const parser = mqtt.parser(opts);
+
+// Synchronously emits all the parsed packets
+parser.on('packet', packet => {
+  console.log(packet)
+  // Prints:
+  //
+  // {
+  //   cmd: 'publish',
+  //   retain: false,
+  //   qos: 0,
+  //   dup: false,
+  //   length: 10,
+  //   topic: 'test',
+  //   payload: <Buffer 74 65 73 74>
+  // }
+})
+
+parser.parse(Buffer.from([
+  48, 10, // Header (publish)
+  0, 4, // Topic length
+  116, 101, 115, 116, // Topic (test)
+  116, 101, 115, 116 // Payload (test)
+]))
+// Returns the number of bytes left in the parser
+```
+
+API
+---
+
+  * <a href="#generate"><code>mqtt#<b>generate()</b></code></a>
+  * <a href="#writeToStream"><code>mqtt#<b>writeToStream()</b></code></a>
+  * <a href="#parser"><code>mqtt#<b>parser()</b></code></a>
+
+<a name="generate">
+
+### mqtt.generate(object, [opts])
+
+Generates a `Buffer` containing an MQTT packet.
+The object must be one of the ones specified by the [packets](#packets)
+section. Throws an `Error` if a packet cannot be generated.
+
+<a name="writeToStream">
+
+### mqtt.writeToStream(object, stream, [opts])
+
+Writes the mqtt packet defined by `object` to the given stream.
+The object must be one of the ones specified by the [packets](#packets)
+section. Emits an `Error` on the stream if a packet cannot be generated.
+On node >= 0.12, this function automatically calls `cork()` on your stream,
+and then it calls `uncork()` on the next tick.
+By default cache for number buffers is enabled.
+It creates a list of buffers for faster write. To disable cache set `mqtt.writeToStream.cacheNumbers = false`.
+Should be set before any `writeToStream` calls.
+
+<a name="parser">
+
+### mqtt.parser([opts])
+
+Returns a new `Parser` object. `Parser` inherits from `EventEmitter` and
+will emit:
+
+  * `packet`, when a new packet is parsed, according to
+    [packets](#packets)
+  * `error`, if an error happens
+
+<a name="parse">
+
+#### Parser.parse(buffer)
+
+Parses a given `Buffer` and emits synchronously all the MQTT packets that
+are included. Returns the number of bytes left to parse.
+
+If an error happens, an `error` event will be emitted, but no `packet` events
+will be emitted after that. Calling `parse()` again clears the error and
+previous buffer, as if you created a new `Parser`.
+
+Packets
+-------
+
+This section describes the format of all packets emitted by the `Parser`
+and that you can input to `generate`.
+
+### Connect
+
+```js
+{
+  cmd: 'connect',
+  protocolId: 'MQTT', // Or 'MQIsdp' in MQTT 3.1 and 5.0
+  protocolVersion: 4, // Or 3 in MQTT 3.1, or 5 in MQTT 5.0
+  clean: true, // Can also be false
+  clientId: 'my-device',
+  keepalive: 0, // Seconds which can be any positive number, with 0 as the default setting
+  username: 'matteo',
+  password: Buffer.from('collina'), // Passwords are buffers
+  will: {
+    topic: 'mydevice/status',
+    payload: Buffer.from('dead'), // Payloads are buffers
+    properties: { // MQTT 5.0
+      willDelayInterval: 1234,
+      payloadFormatIndicator: false,
+      messageExpiryInterval: 4321,
+      contentType: 'test',
+      responseTopic: 'topic',
+      correlationData: Buffer.from([1, 2, 3, 4]),
+      userProperties: {
+        'test': 'test'
+      }
+    }
+  },
+  properties: { // MQTT 5.0 properties
+      sessionExpiryInterval: 1234,
+      receiveMaximum: 432,
+      maximumPacketSize: 100,
+      topicAliasMaximum: 456,
+      requestResponseInformation: true,
+      requestProblemInformation: true,
+      userProperties: {
+        'test': 'test'
+      },
+      authenticationMethod: 'test',
+      authenticationData: Buffer.from([1, 2, 3, 4])
+  }
+}
+```
+
+If `protocolVersion` is 3, `clientId` is mandatory and `generate` will throw if
+missing.
+
+If `password` or `will.payload` are passed as strings, they will
+automatically be converted into a `Buffer`.
+
+### Connack
+
+```js
+{
+  cmd: 'connack',
+  returnCode: 0, // Or whatever else you see fit MQTT < 5.0
+  sessionPresent: false, // Can also be true.
+  reasonCode: 0, // reason code MQTT 5.0
+  properties: { // MQTT 5.0 properties
+      sessionExpiryInterval: 1234,
+      receiveMaximum: 432,
+      maximumQoS: 2,
+      retainAvailable: true,
+      maximumPacketSize: 100,
+      assignedClientIdentifier: 'test',
+      topicAliasMaximum: 456,
+      reasonString: 'test',
+      userProperties: {
+        'test': 'test'
+      },
+      wildcardSubscriptionAvailable: true,
+      subscriptionIdentifiersAvailable: true,
+      sharedSubscriptionAvailable: false,
+      serverKeepAlive: 1234,
+      responseInformation: 'test',
+      serverReference: 'test',
+      authenticationMethod: 'test',
+      authenticationData: Buffer.from([1, 2, 3, 4])
+  }
+}
+```
+
+The only mandatory argument is `returnCode`, as `generate` will throw if
+missing.
+
+### Subscribe
+
+```js
+{
+  cmd: 'subscribe',
+  messageId: 42,
+  properties: { // MQTT 5.0 properties
+    subscriptionIdentifier: 145,
+    userProperties: {
+      test: 'test'
+    }
+  }
+  subscriptions: [{
+    topic: 'test',
+    qos: 0,
+    nl: false, // no Local MQTT 5.0 flag
+    rap: true, // Retain as Published MQTT 5.0 flag
+    rh: 1 // Retain Handling MQTT 5.0
+  }]
+}
+```
+
+All properties are mandatory.
+
+### Suback
+
+```js
+{
+  cmd: 'suback',
+  messageId: 42,
+  properties: { // MQTT 5.0 properties
+    reasonString: 'test',
+    userProperties: {
+      'test': 'test'
+    }
+  }
+  granted: [0, 1, 2, 128]
+}
+```
+
+All the granted qos __must__ be < 256, as they are encoded as UInt8.
+All properties are mandatory.
+
+### Unsubscribe
+
+```js
+{
+  cmd: 'unsubscribe',
+  messageId: 42,
+  properties: { // MQTT 5.0 properties
+    userProperties: {
+      'test': 'test'
+    }
+  }
+  unsubscriptions: [
+    'test',
+    'a/topic'
+  ]
+}
+```
+
+All properties are mandatory.
+
+### Unsuback
+
+```js
+{
+  cmd: 'unsuback',
+  messageId: 42,
+  properties: { // MQTT 5.0 properties
+    reasonString: 'test',
+    userProperties: {
+      'test': 'test'
+    }
+  }
+}
+```
+
+All properties are mandatory.
+
+### Publish
+
+```js
+{
+  cmd: 'publish',
+  messageId: 42,
+  qos: 2,
+  dup: false,
+  topic: 'test',
+  payload: Buffer.from('test'),
+  retain: false,
+  properties: { // optional properties MQTT 5.0
+      payloadFormatIndicator: true,
+      messageExpiryInterval: 4321,
+      topicAlias: 100,
+      responseTopic: 'topic',
+      correlationData: Buffer.from([1, 2, 3, 4]),
+      userProperties: {
+        'test': 'test'
+      },
+      subscriptionIdentifier: 120, // can be an Array in message from broker, if message included in few another subscriptions
+      contentType: 'test'
+   }
+}
+```
+
+Only the `topic` property is mandatory.
+Both `topic` and `payload` can be `Buffer` objects instead of strings.
+`messageId` is mandatory for `qos > 0`.
+
+### Puback
+
+```js
+{
+  cmd: 'puback',
+  messageId: 42,
+  reasonCode: 16, // only for MQTT 5.0
+  properties: { // MQTT 5.0 properties
+      reasonString: 'test',
+      userProperties: {
+        'test': 'test'
+      }
+  }
+}
+```
+
+The only mandatory property is `messageId`, as `generate` will throw if
+missing.
+
+### Pubrec
+
+```js
+{
+  cmd: 'pubrec',
+  messageId: 42,
+  reasonCode: 16, // only for MQTT 5.0
+  properties: { // properties MQTT 5.0
+    reasonString: 'test',
+    userProperties: {
+      'test': 'test'
+    }
+  }
+}
+```
+
+The only mandatory property is `messageId`, as `generate` will throw if
+missing.
+
+### Pubrel
+
+```js
+{
+  cmd: 'pubrel',
+  messageId: 42,
+  reasonCode: 16, // only for MQTT 5.0
+  properties: { // properties MQTT 5.0
+     reasonString: 'test',
+     userProperties: {
+       'test': 'test'
+     }
+  }
+}
+```
+
+The only mandatory property is `messageId`, as `generate` will throw if
+missing.
+
+### Pubcomp
+
+```js
+{
+  cmd: 'pubcomp',
+  messageId: 42,
+  reasonCode: 16, // only for MQTT 5.0
+  properties: { // properties MQTT 5.0
+    reasonString: 'test',
+    userProperties: {
+       'test': 'test'
+    }
+  }
+}
+```
+
+The only mandatory property is `messageId`, as `generate` will throw if
+missing.
+
+### Pingreq
+
+```js
+{
+  cmd: 'pingreq'
+}
+```
+
+### Pingresp
+
+```js
+{
+  cmd: 'pingresp'
+}
+```
+
+### Disconnect
+
+```js
+{
+  cmd: 'disconnect',
+  reasonCode: 0, // MQTT 5.0 code
+  properties: { // properties MQTT 5.0
+     sessionExpiryInterval: 145,
+     reasonString: 'test',
+     userProperties: {
+       'test': 'test'
+     },
+     serverReference: 'test'
+  }
+}
+```
+
+### Auth
+
+```js
+{
+  cmd: 'auth',
+  reasonCode: 0, // MQTT 5.0 code
+  properties: { // properties MQTT 5.0
+     authenticationMethod: 'test',
+     authenticationData: Buffer.from([0, 1, 2, 3]),
+     reasonString: 'test',
+     userProperties: {
+       'test': 'test'
+     }
+  }
+}
+```
+
+<a name="contributing"></a>
+
+Contributing
+------------
+
+mqtt-packet is an **OPEN Open Source Project**. This means that:
+
+> Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project.
+
+See the [CONTRIBUTING.md](https://github.com/mqttjs/mqtt-packet/blob/master/CONTRIBUTING.md) file for more details.
+
+### Contributors
+
+mqtt-packet is only possible due to the excellent work of the following contributors:
+
+<table><tbody>
+<tr><th align="left">Matteo Collina</th><td><a href="https://github.com/mcollina">GitHub/mcollina</a></td><td><a href="http://twitter.com/matteocollina">Twitter/@matteocollina</a></td></tr>
+<tr><th align="left">Adam Rudd</th><td><a href="https://github.com/adamvr">GitHub/adamvr</a></td><td><a href="http://twitter.com/adam_vr">Twitter/@adam_vr</a></td></tr>
+<tr><th align="left">Peter Sorowka</th><td><a href="https://github.com/psorowka">GitHub/psorowka</a></td><td><a href="http://twitter.com/psorowka">Twitter/@psorowka</a></td></tr>
+<tr><th align="left">Siarhei Buntsevich</th><td><a href="https://github.com/scarry1992">GitHub/scarry1992</a></td></tr>
+</tbody></table>
+
+License
+-------
+
+MIT

+ 26 - 0
uni_modules/mqtt-packet/benchmarks/generate.js

@@ -0,0 +1,26 @@
+const mqtt = require('../')
+const max = 100000
+let i
+const buf = Buffer.from('test')
+
+// initialize it
+mqtt.generate({
+  cmd: 'publish',
+  topic: 'test',
+  payload: buf
+})
+
+const start = Date.now()
+
+for (i = 0; i < max; i++) {
+  mqtt.generate({
+    cmd: 'publish',
+    topic: 'test',
+    payload: buf
+  })
+}
+
+const time = Date.now() - start
+console.log('Total time', time)
+console.log('Total packets', max)
+console.log('Packet/s', max / time * 1000)

+ 51 - 0
uni_modules/mqtt-packet/benchmarks/generateNet.js

@@ -0,0 +1,51 @@
+
+const mqtt = require('../')
+const max = 1000000
+let i = 0
+const start = Date.now()
+let time
+const buf = Buffer.allocUnsafe(10)
+const net = require('net')
+const server = net.createServer(handle)
+let dest
+
+buf.fill('test')
+
+function handle (sock) {
+  sock.resume()
+}
+
+server.listen(0, () => {
+  dest = net.connect(server.address())
+
+  dest.on('connect', tickWait)
+  dest.on('drain', tickWait)
+  dest.on('finish', () => {
+    time = Date.now() - start
+    console.log('Total time', time)
+    console.log('Total packets', max)
+    console.log('Packet/s', max / time * 1000)
+    server.close()
+  })
+})
+
+function tickWait () {
+  // console.log('tickWait', i)
+  let res = true
+  // var toSend = new Buffer(5 + buf.length)
+
+  for (; i < max && res; i++) {
+    res = dest.write(mqtt.generate({
+      cmd: 'publish',
+      topic: 'test',
+      payload: buf
+    }))
+    // buf.copy(toSend, 5)
+    // res = dest.write(toSend, 'buffer')
+    // console.log(res)
+  }
+
+  if (i >= max) {
+    dest.end()
+  }
+}

+ 20 - 0
uni_modules/mqtt-packet/benchmarks/parse.js

@@ -0,0 +1,20 @@
+
+const mqtt = require('../')
+const parser = mqtt.parser()
+const max = 10000000
+let i
+const start = Date.now() / 1000
+
+for (i = 0; i < max; i++) {
+  parser.parse(Buffer.from([
+    48, 10, // Header (publish)
+    0, 4, // Topic length
+    116, 101, 115, 116, // Topic (test)
+    116, 101, 115, 116 // Payload (test)
+  ]))
+}
+
+const time = Date.now() / 1000 - start
+console.log('Total packets', max)
+console.log('Total time', Math.round(time * 100) / 100)
+console.log('Packet/s', max / time)

+ 49 - 0
uni_modules/mqtt-packet/benchmarks/writeToStream.js

@@ -0,0 +1,49 @@
+
+const mqtt = require('../')
+const max = 1000000
+let i = 0
+const start = Date.now()
+let time
+const buf = Buffer.allocUnsafe(10)
+const net = require('net')
+const server = net.createServer(handle)
+let dest
+
+function handle (sock) {
+  sock.resume()
+}
+
+buf.fill('test')
+
+server.listen(0, () => {
+  dest = net.connect(server.address())
+
+  dest.on('connect', tickWait)
+  dest.on('drain', tickWait)
+  dest.on('finish', () => {
+    time = Date.now() - start
+    console.log('Total time', time)
+    console.log('Total packets', max)
+    console.log('Packet/s', max / time * 1000)
+    server.close()
+  })
+})
+
+function tickWait () {
+  let res = true
+  // var toSend = new Buffer(5)
+
+  for (; i < max && res; i++) {
+    res = mqtt.writeToStream({
+      cmd: 'publish',
+      topic: 'test',
+      payload: buf
+    }, dest)
+    // dest.write(toSend, 'buffer')
+    // res = dest.write(buf, 'buffer')
+  }
+
+  if (i >= max) {
+    dest.end()
+  }
+}

+ 187 - 0
uni_modules/mqtt-packet/constants.js

@@ -0,0 +1,187 @@
+/* Protocol - protocol constants */
+const protocol = module.exports
+
+/* Command code => mnemonic */
+protocol.types = {
+  0: 'reserved',
+  1: 'connect',
+  2: 'connack',
+  3: 'publish',
+  4: 'puback',
+  5: 'pubrec',
+  6: 'pubrel',
+  7: 'pubcomp',
+  8: 'subscribe',
+  9: 'suback',
+  10: 'unsubscribe',
+  11: 'unsuback',
+  12: 'pingreq',
+  13: 'pingresp',
+  14: 'disconnect',
+  15: 'auth'
+}
+
+/* Mnemonic => Command code */
+protocol.codes = {}
+for (const k in protocol.types) {
+  const v = protocol.types[k]
+  protocol.codes[v] = k
+}
+
+/* Header */
+protocol.CMD_SHIFT = 4
+protocol.CMD_MASK = 0xF0
+protocol.DUP_MASK = 0x08
+protocol.QOS_MASK = 0x03
+protocol.QOS_SHIFT = 1
+protocol.RETAIN_MASK = 0x01
+
+/* Length */
+protocol.VARBYTEINT_MASK = 0x7F
+protocol.VARBYTEINT_FIN_MASK = 0x80
+protocol.VARBYTEINT_MAX = 268435455
+
+/* Connack */
+protocol.SESSIONPRESENT_MASK = 0x01
+protocol.SESSIONPRESENT_HEADER = Buffer.from([protocol.SESSIONPRESENT_MASK])
+protocol.CONNACK_HEADER = Buffer.from([protocol.codes.connack << protocol.CMD_SHIFT])
+
+/* Connect */
+protocol.USERNAME_MASK = 0x80
+protocol.PASSWORD_MASK = 0x40
+protocol.WILL_RETAIN_MASK = 0x20
+protocol.WILL_QOS_MASK = 0x18
+protocol.WILL_QOS_SHIFT = 3
+protocol.WILL_FLAG_MASK = 0x04
+protocol.CLEAN_SESSION_MASK = 0x02
+protocol.CONNECT_HEADER = Buffer.from([protocol.codes.connect << protocol.CMD_SHIFT])
+
+/* Properties */
+protocol.properties = {
+  sessionExpiryInterval: 17,
+  willDelayInterval: 24,
+  receiveMaximum: 33,
+  maximumPacketSize: 39,
+  topicAliasMaximum: 34,
+  requestResponseInformation: 25,
+  requestProblemInformation: 23,
+  userProperties: 38,
+  authenticationMethod: 21,
+  authenticationData: 22,
+  payloadFormatIndicator: 1,
+  messageExpiryInterval: 2,
+  contentType: 3,
+  responseTopic: 8,
+  correlationData: 9,
+  maximumQoS: 36,
+  retainAvailable: 37,
+  assignedClientIdentifier: 18,
+  reasonString: 31,
+  wildcardSubscriptionAvailable: 40,
+  subscriptionIdentifiersAvailable: 41,
+  sharedSubscriptionAvailable: 42,
+  serverKeepAlive: 19,
+  responseInformation: 26,
+  serverReference: 28,
+  topicAlias: 35,
+  subscriptionIdentifier: 11
+}
+protocol.propertiesCodes = {}
+for (const prop in protocol.properties) {
+  const id = protocol.properties[prop]
+  protocol.propertiesCodes[id] = prop
+}
+protocol.propertiesTypes = {
+  sessionExpiryInterval: 'int32',
+  willDelayInterval: 'int32',
+  receiveMaximum: 'int16',
+  maximumPacketSize: 'int32',
+  topicAliasMaximum: 'int16',
+  requestResponseInformation: 'byte',
+  requestProblemInformation: 'byte',
+  userProperties: 'pair',
+  authenticationMethod: 'string',
+  authenticationData: 'binary',
+  payloadFormatIndicator: 'byte',
+  messageExpiryInterval: 'int32',
+  contentType: 'string',
+  responseTopic: 'string',
+  correlationData: 'binary',
+  maximumQoS: 'int8',
+  retainAvailable: 'byte',
+  assignedClientIdentifier: 'string',
+  reasonString: 'string',
+  wildcardSubscriptionAvailable: 'byte',
+  subscriptionIdentifiersAvailable: 'byte',
+  sharedSubscriptionAvailable: 'byte',
+  serverKeepAlive: 'int16',
+  responseInformation: 'string',
+  serverReference: 'string',
+  topicAlias: 'int16',
+  subscriptionIdentifier: 'var'
+}
+
+function genHeader (type) {
+  return [0, 1, 2].map(qos => {
+    return [0, 1].map(dup => {
+      return [0, 1].map(retain => {
+        const buf = Buffer.alloc(1)
+        buf.writeUInt8(
+          protocol.codes[type] << protocol.CMD_SHIFT |
+          (dup ? protocol.DUP_MASK : 0) |
+          qos << protocol.QOS_SHIFT | retain, 0, true)
+        return buf
+      })
+    })
+  })
+}
+
+/* Publish */
+protocol.PUBLISH_HEADER = genHeader('publish')
+
+/* Subscribe */
+protocol.SUBSCRIBE_HEADER = genHeader('subscribe')
+protocol.SUBSCRIBE_OPTIONS_QOS_MASK = 0x03
+protocol.SUBSCRIBE_OPTIONS_NL_MASK = 0x01
+protocol.SUBSCRIBE_OPTIONS_NL_SHIFT = 2
+protocol.SUBSCRIBE_OPTIONS_RAP_MASK = 0x01
+protocol.SUBSCRIBE_OPTIONS_RAP_SHIFT = 3
+protocol.SUBSCRIBE_OPTIONS_RH_MASK = 0x03
+protocol.SUBSCRIBE_OPTIONS_RH_SHIFT = 4
+protocol.SUBSCRIBE_OPTIONS_RH = [0x00, 0x10, 0x20]
+protocol.SUBSCRIBE_OPTIONS_NL = 0x04
+protocol.SUBSCRIBE_OPTIONS_RAP = 0x08
+protocol.SUBSCRIBE_OPTIONS_QOS = [0x00, 0x01, 0x02]
+
+/* Unsubscribe */
+protocol.UNSUBSCRIBE_HEADER = genHeader('unsubscribe')
+
+/* Confirmations */
+protocol.ACKS = {
+  unsuback: genHeader('unsuback'),
+  puback: genHeader('puback'),
+  pubcomp: genHeader('pubcomp'),
+  pubrel: genHeader('pubrel'),
+  pubrec: genHeader('pubrec')
+}
+
+protocol.SUBACK_HEADER = Buffer.from([protocol.codes.suback << protocol.CMD_SHIFT])
+
+/* Protocol versions */
+protocol.VERSION3 = Buffer.from([3])
+protocol.VERSION4 = Buffer.from([4])
+protocol.VERSION5 = Buffer.from([5])
+protocol.VERSION131 = Buffer.from([131])
+protocol.VERSION132 = Buffer.from([132])
+
+/* QoS */
+protocol.QOS = [0, 1, 2].map(qos => {
+  return Buffer.from([qos])
+})
+
+/* Empty packets */
+protocol.EMPTY = {
+  pingreq: Buffer.from([protocol.codes.pingreq << 4, 0]),
+  pingresp: Buffer.from([protocol.codes.pingresp << 4, 0]),
+  disconnect: Buffer.from([protocol.codes.disconnect << 4, 0])
+}

+ 52 - 0
uni_modules/mqtt-packet/generate.js

@@ -0,0 +1,52 @@
+const writeToStream = require('./writeToStream')
+const EventEmitter = require('events')
+
+function generate (packet, opts) {
+  const stream = new Accumulator()
+  writeToStream(packet, stream, opts)
+  return stream.concat()
+}
+
+class Accumulator extends EventEmitter {
+  constructor () {
+    super()
+    this._array = new Array(20)
+    this._i = 0
+  }
+
+  write (chunk) {
+    this._array[this._i++] = chunk
+    return true
+  }
+
+  concat () {
+    let length = 0
+    const lengths = new Array(this._array.length)
+    const list = this._array
+    let pos = 0
+    let i
+
+    for (i = 0; i < list.length && list[i] !== undefined; i++) {
+      if (typeof list[i] !== 'string') lengths[i] = list[i].length
+      else lengths[i] = Buffer.byteLength(list[i])
+
+      length += lengths[i]
+    }
+
+    const result = Buffer.allocUnsafe(length)
+
+    for (i = 0; i < list.length && list[i] !== undefined; i++) {
+      if (typeof list[i] !== 'string') {
+        list[i].copy(result, pos)
+        pos += lengths[i]
+      } else {
+        result.write(list[i], pos)
+        pos += lengths[i]
+      }
+    }
+
+    return result
+  }
+}
+
+module.exports = generate

+ 3 - 0
uni_modules/mqtt-packet/mqtt.js

@@ -0,0 +1,3 @@
+exports.parser = require('./parser').parser
+exports.generate = require('./generate')
+exports.writeToStream = require('./writeToStream')

+ 58 - 0
uni_modules/mqtt-packet/numbers.js

@@ -0,0 +1,58 @@
+const max = 65536
+const cache = {}
+
+// in node 6 Buffer.subarray returns a Uint8Array instead of a Buffer
+// later versions return a Buffer
+// alternative is Buffer.slice but that creates a new buffer
+// creating new buffers takes time
+// SubOk is only false on node < 8
+const SubOk = Buffer.isBuffer(Buffer.from([1, 2]).subarray(0, 1))
+
+function generateBuffer (i) {
+  const buffer = Buffer.allocUnsafe(2)
+  buffer.writeUInt8(i >> 8, 0)
+  buffer.writeUInt8(i & 0x00FF, 0 + 1)
+
+  return buffer
+}
+
+function generateCache () {
+  for (let i = 0; i < max; i++) {
+    cache[i] = generateBuffer(i)
+  }
+}
+
+function genBufVariableByteInt (num) {
+  const maxLength = 4 // max 4 bytes
+  let digit = 0
+  let pos = 0
+  const buffer = Buffer.allocUnsafe(maxLength)
+
+  do {
+    digit = num % 128 | 0
+    num = num / 128 | 0
+    if (num > 0) digit = digit | 0x80
+
+    buffer.writeUInt8(digit, pos++)
+  } while (num > 0 && pos < maxLength)
+
+  if (num > 0) {
+    pos = 0
+  }
+
+  return SubOk ? buffer.subarray(0, pos) : buffer.slice(0, pos)
+}
+
+function generate4ByteBuffer (num) {
+  const buffer = Buffer.allocUnsafe(4)
+  buffer.writeUInt32BE(num, 0)
+  return buffer
+}
+
+module.exports = {
+  cache,
+  generateCache,
+  generateNumber: generateBuffer,
+  genBufVariableByteInt,
+  generate4ByteBuffer
+}

+ 48 - 0
uni_modules/mqtt-packet/package.json

@@ -0,0 +1,48 @@
+{
+  "name": "mqtt-packet",
+  "version": "6.10.0",
+  "description": "Parse and generate MQTT packets like a breeze",
+  "main": "mqtt.js",
+  "types": "types/index.d.ts",
+  "contributors": [
+    "Matteo Collina <matteo.collina@gmail.com> (https://github.com/mcollina)",
+    "Adam Rudd <damvrr@gmail.com>",
+    "Peter Sorowka (https://github.com/psorowka)",
+    "Wouter Klijn <contact@wuhkuh.com> (https://github.com/wuhkuh)",
+    "Siarhei Buntsevich (https://github.com/scarry1992)"
+  ],
+  "scripts": {
+    "test": "tape test.js | tap-spec && standard",
+    "ci": "tape test.js && node testRandom && standard"
+  },
+  "pre-commit": "test",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/mqttjs/mqtt-packet.git"
+  },
+  "keywords": [
+    "MQTT",
+    "packet",
+    "parse",
+    "publish",
+    "subscribe",
+    "pubsub"
+  ],
+  "license": "MIT",
+  "bugs": {
+    "url": "https://github.com/mqttjs/mqtt-packet/issues"
+  },
+  "homepage": "https://github.com/mqttjs/mqtt-packet",
+  "devDependencies": {
+    "pre-commit": "^1.2.2",
+    "readable-stream": "^3.6.0",
+    "standard": "^14.3.4",
+    "tap-spec": "^5.0.0",
+    "tape": "^5.0.1"
+  },
+  "dependencies": {
+    "bl": "^4.0.2",
+    "debug": "^4.1.1",
+    "process-nextick-args": "^2.0.1"
+  }
+}

+ 13 - 0
uni_modules/mqtt-packet/packet.js

@@ -0,0 +1,13 @@
+class Packet {
+  constructor () {
+    this.cmd = null
+    this.retain = false
+    this.qos = 0
+    this.dup = false
+    this.length = -1
+    this.topic = null
+    this.payload = null
+  }
+}
+
+module.exports = Packet

+ 716 - 0
uni_modules/mqtt-packet/parser.js

@@ -0,0 +1,716 @@
+const bl = require('bl')
+const EventEmitter = require('events')
+const Packet = require('./packet')
+const constants = require('./constants')
+const debug = require('debug')('mqtt-packet:parser')
+
+class Parser extends EventEmitter {
+  constructor () {
+    super()
+    this.parser = this.constructor.parser
+  }
+
+  static parser (opt) {
+    if (!(this instanceof Parser)) return (new Parser()).parser(opt)
+
+    this.settings = opt || {}
+
+    this._states = [
+      '_parseHeader',
+      '_parseLength',
+      '_parsePayload',
+      '_newPacket'
+    ]
+
+    this._resetState()
+    return this
+  }
+
+  _resetState () {
+    debug('_resetState: resetting packet, error, _list, and _stateCounter')
+    this.packet = new Packet()
+    this.error = null
+    this._list = bl()
+    this._stateCounter = 0
+  }
+
+  parse (buf) {
+    if (this.error) this._resetState()
+
+    this._list.append(buf)
+    debug('parse: current state: %s', this._states[this._stateCounter])
+    while ((this.packet.length !== -1 || this._list.length > 0) &&
+      this[this._states[this._stateCounter]]() &&
+      !this.error) {
+      this._stateCounter++
+      debug('parse: state complete. _stateCounter is now: %d', this._stateCounter)
+      debug('parse: packet.length: %d, buffer list length: %d', this.packet.length, this._list.length)
+      if (this._stateCounter >= this._states.length) this._stateCounter = 0
+    }
+    debug('parse: exited while loop. packet: %d, buffer list length: %d', this.packet.length, this._list.length)
+    return this._list.length
+  }
+
+  _parseHeader () {
+    // There is at least one byte in the buffer
+    const zero = this._list.readUInt8(0)
+    this.packet.cmd = constants.types[zero >> constants.CMD_SHIFT]
+    this.packet.retain = (zero & constants.RETAIN_MASK) !== 0
+    this.packet.qos = (zero >> constants.QOS_SHIFT) & constants.QOS_MASK
+    this.packet.dup = (zero & constants.DUP_MASK) !== 0
+    debug('_parseHeader: packet: %o', this.packet)
+
+    this._list.consume(1)
+
+    return true
+  }
+
+  _parseLength () {
+    // There is at least one byte in the list
+    const result = this._parseVarByteNum(true)
+
+    if (result) {
+      this.packet.length = result.value
+      this._list.consume(result.bytes)
+    }
+    debug('_parseLength %d', result.value)
+    return !!result
+  }
+
+  _parsePayload () {
+    debug('_parsePayload: payload %O', this._list)
+    let result = false
+
+    // Do we have a payload? Do we have enough data to complete the payload?
+    // PINGs have no payload
+    if (this.packet.length === 0 || this._list.length >= this.packet.length) {
+      this._pos = 0
+
+      switch (this.packet.cmd) {
+        case 'connect':
+          this._parseConnect()
+          break
+        case 'connack':
+          this._parseConnack()
+          break
+        case 'publish':
+          this._parsePublish()
+          break
+        case 'puback':
+        case 'pubrec':
+        case 'pubrel':
+        case 'pubcomp':
+          this._parseConfirmation()
+          break
+        case 'subscribe':
+          this._parseSubscribe()
+          break
+        case 'suback':
+          this._parseSuback()
+          break
+        case 'unsubscribe':
+          this._parseUnsubscribe()
+          break
+        case 'unsuback':
+          this._parseUnsuback()
+          break
+        case 'pingreq':
+        case 'pingresp':
+          // These are empty, nothing to do
+          break
+        case 'disconnect':
+          this._parseDisconnect()
+          break
+        case 'auth':
+          this._parseAuth()
+          break
+        default:
+          this._emitError(new Error('Not supported'))
+      }
+
+      result = true
+    }
+    debug('_parsePayload complete result: %s', result)
+    return result
+  }
+
+  _parseConnect () {
+    debug('_parseConnect')
+    let topic // Will topic
+    let payload // Will payload
+    let password // Password
+    let username // Username
+    const flags = {}
+    const packet = this.packet
+
+    // Parse protocolId
+    const protocolId = this._parseString()
+
+    if (protocolId === null) return this._emitError(new Error('Cannot parse protocolId'))
+    if (protocolId !== 'MQTT' && protocolId !== 'MQIsdp') {
+      return this._emitError(new Error('Invalid protocolId'))
+    }
+
+    packet.protocolId = protocolId
+
+    // Parse constants version number
+    if (this._pos >= this._list.length) return this._emitError(new Error('Packet too short'))
+
+    packet.protocolVersion = this._list.readUInt8(this._pos)
+
+    if (packet.protocolVersion >= 128) {
+      packet.bridgeMode = true
+      packet.protocolVersion = packet.protocolVersion - 128
+    }
+
+    if (packet.protocolVersion !== 3 && packet.protocolVersion !== 4 && packet.protocolVersion !== 5) {
+      return this._emitError(new Error('Invalid protocol version'))
+    }
+
+    this._pos++
+
+    if (this._pos >= this._list.length) {
+      return this._emitError(new Error('Packet too short'))
+    }
+
+    // Parse connect flags
+    flags.username = (this._list.readUInt8(this._pos) & constants.USERNAME_MASK)
+    flags.password = (this._list.readUInt8(this._pos) & constants.PASSWORD_MASK)
+    flags.will = (this._list.readUInt8(this._pos) & constants.WILL_FLAG_MASK)
+
+    if (flags.will) {
+      packet.will = {}
+      packet.will.retain = (this._list.readUInt8(this._pos) & constants.WILL_RETAIN_MASK) !== 0
+      packet.will.qos = (this._list.readUInt8(this._pos) &
+        constants.WILL_QOS_MASK) >> constants.WILL_QOS_SHIFT
+    }
+
+    packet.clean = (this._list.readUInt8(this._pos) & constants.CLEAN_SESSION_MASK) !== 0
+    this._pos++
+
+    // Parse keepalive
+    packet.keepalive = this._parseNum()
+    if (packet.keepalive === -1) return this._emitError(new Error('Packet too short'))
+
+    // parse properties
+    if (packet.protocolVersion === 5) {
+      const properties = this._parseProperties()
+      if (Object.getOwnPropertyNames(properties).length) {
+        packet.properties = properties
+      }
+    }
+    // Parse clientId
+    const clientId = this._parseString()
+    if (clientId === null) return this._emitError(new Error('Packet too short'))
+    packet.clientId = clientId
+    debug('_parseConnect: packet.clientId: %s', packet.clientId)
+
+    if (flags.will) {
+      if (packet.protocolVersion === 5) {
+        const willProperties = this._parseProperties()
+        if (Object.getOwnPropertyNames(willProperties).length) {
+          packet.will.properties = willProperties
+        }
+      }
+      // Parse will topic
+      topic = this._parseString()
+      if (topic === null) return this._emitError(new Error('Cannot parse will topic'))
+      packet.will.topic = topic
+      debug('_parseConnect: packet.will.topic: %s', packet.will.topic)
+
+      // Parse will payload
+      payload = this._parseBuffer()
+      if (payload === null) return this._emitError(new Error('Cannot parse will payload'))
+      packet.will.payload = payload
+      debug('_parseConnect: packet.will.paylaod: %s', packet.will.payload)
+    }
+
+    // Parse username
+    if (flags.username) {
+      username = this._parseString()
+      if (username === null) return this._emitError(new Error('Cannot parse username'))
+      packet.username = username
+      debug('_parseConnect: packet.username: %s', packet.username)
+    }
+
+    // Parse password
+    if (flags.password) {
+      password = this._parseBuffer()
+      if (password === null) return this._emitError(new Error('Cannot parse password'))
+      packet.password = password
+    }
+    // need for right parse auth packet and self set up
+    this.settings = packet
+    debug('_parseConnect: complete')
+    return packet
+  }
+
+  _parseConnack () {
+    debug('_parseConnack')
+    const packet = this.packet
+
+    if (this._list.length < 1) return null
+    packet.sessionPresent = !!(this._list.readUInt8(this._pos++) & constants.SESSIONPRESENT_MASK)
+
+    if (this.settings.protocolVersion === 5) {
+      if (this._list.length >= 2) {
+        packet.reasonCode = this._list.readUInt8(this._pos++)
+      } else {
+        packet.reasonCode = 0
+      }
+    } else {
+      if (this._list.length < 2) return null
+      packet.returnCode = this._list.readUInt8(this._pos++)
+    }
+
+    if (packet.returnCode === -1 || packet.reasonCode === -1) return this._emitError(new Error('Cannot parse return code'))
+    // mqtt 5 properties
+    if (this.settings.protocolVersion === 5) {
+      const properties = this._parseProperties()
+      if (Object.getOwnPropertyNames(properties).length) {
+        packet.properties = properties
+      }
+    }
+    debug('_parseConnack: complete')
+  }
+
+  _parsePublish () {
+    debug('_parsePublish')
+    const packet = this.packet
+    packet.topic = this._parseString()
+
+    if (packet.topic === null) return this._emitError(new Error('Cannot parse topic'))
+
+    // Parse messageId
+    if (packet.qos > 0) if (!this._parseMessageId()) { return }
+
+    // Properties mqtt 5
+    if (this.settings.protocolVersion === 5) {
+      const properties = this._parseProperties()
+      if (Object.getOwnPropertyNames(properties).length) {
+        packet.properties = properties
+      }
+    }
+
+    packet.payload = this._list.slice(this._pos, packet.length)
+    debug('_parsePublish: payload from buffer list: %o', packet.payload)
+  }
+
+  _parseSubscribe () {
+    debug('_parseSubscribe')
+    const packet = this.packet
+    let topic
+    let options
+    let qos
+    let rh
+    let rap
+    let nl
+    let subscription
+
+    if (packet.qos !== 1) {
+      return this._emitError(new Error('Wrong subscribe header'))
+    }
+
+    packet.subscriptions = []
+
+    if (!this._parseMessageId()) { return }
+
+    // Properties mqtt 5
+    if (this.settings.protocolVersion === 5) {
+      const properties = this._parseProperties()
+      if (Object.getOwnPropertyNames(properties).length) {
+        packet.properties = properties
+      }
+    }
+
+    while (this._pos < packet.length) {
+      // Parse topic
+      topic = this._parseString()
+      if (topic === null) return this._emitError(new Error('Cannot parse topic'))
+      if (this._pos >= packet.length) return this._emitError(new Error('Malformed Subscribe Payload'))
+
+      options = this._parseByte()
+      qos = options & constants.SUBSCRIBE_OPTIONS_QOS_MASK
+      nl = ((options >> constants.SUBSCRIBE_OPTIONS_NL_SHIFT) & constants.SUBSCRIBE_OPTIONS_NL_MASK) !== 0
+      rap = ((options >> constants.SUBSCRIBE_OPTIONS_RAP_SHIFT) & constants.SUBSCRIBE_OPTIONS_RAP_MASK) !== 0
+      rh = (options >> constants.SUBSCRIBE_OPTIONS_RH_SHIFT) & constants.SUBSCRIBE_OPTIONS_RH_MASK
+
+      subscription = { topic, qos }
+
+      // mqtt 5 options
+      if (this.settings.protocolVersion === 5) {
+        subscription.nl = nl
+        subscription.rap = rap
+        subscription.rh = rh
+      } else if (this.settings.bridgeMode) {
+        subscription.rh = 0
+        subscription.rap = true
+        subscription.nl = true
+      }
+
+      // Push pair to subscriptions
+      debug('_parseSubscribe: push subscription `%s` to subscription', subscription)
+      packet.subscriptions.push(subscription)
+    }
+  }
+
+  _parseSuback () {
+    debug('_parseSuback')
+    const packet = this.packet
+    this.packet.granted = []
+
+    if (!this._parseMessageId()) { return }
+
+    // Properties mqtt 5
+    if (this.settings.protocolVersion === 5) {
+      const properties = this._parseProperties()
+      if (Object.getOwnPropertyNames(properties).length) {
+        packet.properties = properties
+      }
+    }
+
+    // Parse granted QoSes
+    while (this._pos < this.packet.length) {
+      this.packet.granted.push(this._list.readUInt8(this._pos++))
+    }
+  }
+
+  _parseUnsubscribe () {
+    debug('_parseUnsubscribe')
+    const packet = this.packet
+
+    packet.unsubscriptions = []
+
+    // Parse messageId
+    if (!this._parseMessageId()) { return }
+
+    // Properties mqtt 5
+    if (this.settings.protocolVersion === 5) {
+      const properties = this._parseProperties()
+      if (Object.getOwnPropertyNames(properties).length) {
+        packet.properties = properties
+      }
+    }
+
+    while (this._pos < packet.length) {
+      // Parse topic
+      const topic = this._parseString()
+      if (topic === null) return this._emitError(new Error('Cannot parse topic'))
+
+      // Push topic to unsubscriptions
+      debug('_parseUnsubscribe: push topic `%s` to unsubscriptions', topic)
+      packet.unsubscriptions.push(topic)
+    }
+  }
+
+  _parseUnsuback () {
+    debug('_parseUnsuback')
+    const packet = this.packet
+    if (!this._parseMessageId()) return this._emitError(new Error('Cannot parse messageId'))
+    // Properties mqtt 5
+    if (this.settings.protocolVersion === 5) {
+      const properties = this._parseProperties()
+      if (Object.getOwnPropertyNames(properties).length) {
+        packet.properties = properties
+      }
+      // Parse granted QoSes
+      packet.granted = []
+      while (this._pos < this.packet.length) {
+        this.packet.granted.push(this._list.readUInt8(this._pos++))
+      }
+    }
+  }
+
+  // parse packets like puback, pubrec, pubrel, pubcomp
+  _parseConfirmation () {
+    debug('_parseConfirmation: packet.cmd: `%s`', this.packet.cmd)
+    const packet = this.packet
+
+    this._parseMessageId()
+
+    if (this.settings.protocolVersion === 5) {
+      if (packet.length > 2) {
+        // response code
+        packet.reasonCode = this._parseByte()
+        debug('_parseConfirmation: packet.reasonCode `%d`', packet.reasonCode)
+      } else {
+        packet.reasonCode = 0
+      }
+
+      if (packet.length > 3) {
+        // properies mqtt 5
+        const properties = this._parseProperties()
+        if (Object.getOwnPropertyNames(properties).length) {
+          packet.properties = properties
+        }
+      }
+    }
+
+    return true
+  }
+
+  // parse disconnect packet
+  _parseDisconnect () {
+    const packet = this.packet
+    debug('_parseDisconnect')
+
+    if (this.settings.protocolVersion === 5) {
+      // response code
+      if (this._list.length > 0) {
+        packet.reasonCode = this._parseByte()
+      } else {
+        packet.reasonCode = 0
+      }
+      // properies mqtt 5
+      const properties = this._parseProperties()
+      if (Object.getOwnPropertyNames(properties).length) {
+        packet.properties = properties
+      }
+    }
+
+    debug('_parseDisconnect result: true')
+    return true
+  }
+
+  // parse auth packet
+  _parseAuth () {
+    debug('_parseAuth')
+    const packet = this.packet
+
+    if (this.settings.protocolVersion !== 5) {
+      return this._emitError(new Error('Not supported auth packet for this version MQTT'))
+    }
+
+    // response code
+    packet.reasonCode = this._parseByte()
+    // properies mqtt 5
+    const properties = this._parseProperties()
+    if (Object.getOwnPropertyNames(properties).length) {
+      packet.properties = properties
+    }
+
+    debug('_parseAuth: result: true')
+    return true
+  }
+
+  _parseMessageId () {
+    const packet = this.packet
+
+    packet.messageId = this._parseNum()
+
+    if (packet.messageId === null) {
+      this._emitError(new Error('Cannot parse messageId'))
+      return false
+    }
+
+    debug('_parseMessageId: packet.messageId %d', packet.messageId)
+    return true
+  }
+
+  _parseString (maybeBuffer) {
+    const length = this._parseNum()
+    const end = length + this._pos
+
+    if (length === -1 || end > this._list.length || end > this.packet.length) return null
+
+    const result = this._list.toString('utf8', this._pos, end)
+    this._pos += length
+    debug('_parseString: result: %s', result)
+    return result
+  }
+
+  _parseStringPair () {
+    debug('_parseStringPair')
+    return {
+      name: this._parseString(),
+      value: this._parseString()
+    }
+  }
+
+  _parseBuffer () {
+    const length = this._parseNum()
+    const end = length + this._pos
+
+    if (length === -1 || end > this._list.length || end > this.packet.length) return null
+
+    const result = this._list.slice(this._pos, end)
+
+    this._pos += length
+    debug('_parseBuffer: result: %o', result)
+    return result
+  }
+
+  _parseNum () {
+    if (this._list.length - this._pos < 2) return -1
+
+    const result = this._list.readUInt16BE(this._pos)
+    this._pos += 2
+    debug('_parseNum: result: %s', result)
+    return result
+  }
+
+  _parse4ByteNum () {
+    if (this._list.length - this._pos < 4) return -1
+
+    const result = this._list.readUInt32BE(this._pos)
+    this._pos += 4
+    debug('_parse4ByteNum: result: %s', result)
+    return result
+  }
+
+  _parseVarByteNum (fullInfoFlag) {
+    debug('_parseVarByteNum')
+    const maxBytes = 4
+    let bytes = 0
+    let mul = 1
+    let value = 0
+    let result = false
+    let current
+    const padding = this._pos ? this._pos : 0
+
+    while (bytes < maxBytes && (padding + bytes) < this._list.length) {
+      current = this._list.readUInt8(padding + bytes++)
+      value += mul * (current & constants.VARBYTEINT_MASK)
+      mul *= 0x80
+
+      if ((current & constants.VARBYTEINT_FIN_MASK) === 0) {
+        result = true
+        break
+      }
+      if (this._list.length <= bytes) {
+        break
+      }
+    }
+
+    if (!result && bytes === maxBytes && this._list.length >= bytes) {
+      this._emitError(new Error('Invalid variable byte integer'))
+    }
+
+    if (padding) {
+      this._pos += bytes
+    }
+
+    result = result
+      ? fullInfoFlag ? {
+        bytes,
+        value
+      } : value
+      : false
+
+    debug('_parseVarByteNum: result: %o', result)
+    return result
+  }
+
+  _parseByte () {
+    let result
+    if (this._pos < this._list.length) {
+      result = this._list.readUInt8(this._pos)
+      this._pos++
+    }
+    debug('_parseByte: result: %o', result)
+    return result
+  }
+
+  _parseByType (type) {
+    debug('_parseByType: type: %s', type)
+    switch (type) {
+      case 'byte': {
+        return this._parseByte() !== 0
+      }
+      case 'int8': {
+        return this._parseByte()
+      }
+      case 'int16': {
+        return this._parseNum()
+      }
+      case 'int32': {
+        return this._parse4ByteNum()
+      }
+      case 'var': {
+        return this._parseVarByteNum()
+      }
+      case 'string': {
+        return this._parseString()
+      }
+      case 'pair': {
+        return this._parseStringPair()
+      }
+      case 'binary': {
+        return this._parseBuffer()
+      }
+    }
+  }
+
+  _parseProperties () {
+    debug('_parseProperties')
+    const length = this._parseVarByteNum()
+    const start = this._pos
+    const end = start + length
+    const result = {}
+    while (this._pos < end) {
+      const type = this._parseByte()
+      if (!type) {
+        this._emitError(new Error('Cannot parse property code type'))
+        return false
+      }
+      const name = constants.propertiesCodes[type]
+      if (!name) {
+        this._emitError(new Error('Unknown property'))
+        return false
+      }
+      // user properties process
+      if (name === 'userProperties') {
+        if (!result[name]) {
+          result[name] = Object.create(null)
+        }
+        const currentUserProperty = this._parseByType(constants.propertiesTypes[name])
+        if (result[name][currentUserProperty.name]) {
+          if (Array.isArray(result[name][currentUserProperty.name])) {
+            result[name][currentUserProperty.name].push(currentUserProperty.value)
+          } else {
+            const currentValue = result[name][currentUserProperty.name]
+            result[name][currentUserProperty.name] = [currentValue]
+            result[name][currentUserProperty.name].push(currentUserProperty.value)
+          }
+        } else {
+          result[name][currentUserProperty.name] = currentUserProperty.value
+        }
+        continue
+      }
+      if (result[name]) {
+        if (Array.isArray(result[name])) {
+          result[name].push(this._parseByType(constants.propertiesTypes[name]))
+        } else {
+          result[name] = [result[name]]
+          result[name].push(this._parseByType(constants.propertiesTypes[name]))
+        }
+      } else {
+        result[name] = this._parseByType(constants.propertiesTypes[name])
+      }
+    }
+    return result
+  }
+
+  _newPacket () {
+    debug('_newPacket')
+    if (this.packet) {
+      this._list.consume(this.packet.length)
+      debug('_newPacket: parser emit packet: packet.cmd: %s, packet.payload: %s, packet.length: %d', this.packet.cmd, this.packet.payload, this.packet.length)
+      this.emit('packet', this.packet)
+    }
+    debug('_newPacket: new packet')
+    this.packet = new Packet()
+
+    this._pos = 0
+
+    return true
+  }
+
+  _emitError (err) {
+    debug('_emitError')
+    this.error = err
+    this.emit('error', err)
+  }
+}
+
+module.exports = Parser

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 2866 - 0
uni_modules/mqtt-packet/test.js


+ 86 - 0
uni_modules/mqtt-packet/testRandom.js

@@ -0,0 +1,86 @@
+const mqtt = require('./')
+const crypto = require('crypto')
+const max = 1E5
+const start = Date.now() / 1000
+var errors = 0
+var packets = 0
+var randomPacket
+const firstBytes = [
+  16 * 1, // CONNECT
+  16 * 2, // CONNACK
+  16 * 3, // PUBLISH, QoS: 0, No Retain, No Dup
+  16 * 3 + 1, // PUBLISH, QoS: 0, Retain, No Dup
+  16 * 3 + 8, // PUBLISH, QoS: 0, No Retain, Dup
+  16 * 3 + 1 + 8, // PUBLISH, QoS: 0, Retain, Dup
+  16 * 3 + 2, // PUBLISH, QoS: 1, No Retain, No Dup
+  16 * 3 + 2 + 1, // PUBLISH, QoS: 1, Retain, No Dup
+  16 * 3 + 2 + 8, // PUBLISH, QoS: 1, No Retain, Dup
+  16 * 3 + 2 + 1 + 8, // PUBLISH, QoS: 1, Retain, Dup
+  16 * 3 + 4, // PUBLISH, QoS: 2, No Retain, No Dup
+  16 * 3 + 4 + 1, // PUBLISH, QoS: 2, Retain, No Dup
+  16 * 3 + 4 + 8, // PUBLISH, QoS: 2, No Retain, Dup
+  16 * 3 + 4 + 1 + 8, // PUBLISH, QoS: 2, Retain, Dup
+  16 * 4, // PUBACK
+  16 * 5, // PUBREC
+  16 * 6, // PUBREL
+  16 * 7, // PUBCOMP
+  16 * 8, // SUBSCRIBE
+  16 * 9, // SUBACK
+  16 * 10, // UNSUBSCRIBE
+  16 * 11, // UNSUBACK
+  16 * 12, // PINGREQ
+  16 * 13, // PINGRESP
+  16 * 14, // DISCONNECT
+  16 * 15 // RESERVED
+]
+
+function doParse () {
+  const parser = mqtt.parser()
+
+  parser.on('error', onError)
+  parser.on('packet', onPacket)
+  randomPacket = crypto.randomBytes(Math.floor(Math.random() * 512))
+
+  // Increase probability to have a valid first byte in order to at least
+  // enter the parser
+  if (Math.random() > 0.2 && randomPacket.length > 0) randomPacket.writeUInt8(firstBytes[Math.floor(Math.random() * firstBytes.length)], 0)
+  parser.parse(randomPacket)
+}
+
+try {
+  console.log('Starting benchmark')
+  for (let i = 0; i < max; i++) {
+    doParse()
+  }
+} catch (e) {
+  console.log('Exception occurred at packet')
+  console.log(randomPacket)
+  console.log(e.message)
+  console.log(e.stack)
+}
+
+function onError () {
+  errors++
+}
+
+function onPacket () {
+  packets++
+}
+
+const delta = Math.abs(max - packets - errors)
+const time = Date.now() / 1000 - start
+console.log('Benchmark complete')
+console.log('==========================')
+console.log('Sent packets:', max)
+console.log('Total time:', Math.round(time * 100) / 100, 'seconds', '\r\n')
+
+console.log('Valid packets:', packets)
+console.log('Erroneous packets:', errors)
+
+if ((max - packets - errors) < 0) console.log('Excess packets:', delta, '\r\n')
+else console.log('Missing packets:', delta, '\r\n')
+
+console.log('Total packets:', packets + errors)
+console.log('Total errors:', errors + delta)
+console.log('Error rate:', `${((errors + delta) / max * 100).toFixed(2)}%`)
+console.log('==========================')

+ 255 - 0
uni_modules/mqtt-packet/types/index.d.ts

@@ -0,0 +1,255 @@
+import EventEmitter = NodeJS.EventEmitter
+import WritableStream = NodeJS.WritableStream
+
+export declare type QoS = 0 | 1 | 2
+
+export declare type PacketCmd = 'auth' |
+  'connack' |
+  'connect' |
+  'disconnect' |
+  'pingreq' |
+  'pingresp' |
+  'puback' |
+  'pubcomp' |
+  'publish' |
+  'pubrel' |
+  'pubrec' |
+  'suback' |
+  'subscribe' |
+  'unsuback' |
+  'unsubscribe'
+
+export declare type UserProperties = {[index: string]: string | string[]}
+
+export interface IPacket {
+  cmd: PacketCmd
+  messageId?: number
+  length?: number
+}
+
+export interface IAuthPacket extends IPacket {
+  cmd: 'auth'
+  reasonCode: number,
+  properties?: {
+    authenticationMethod?: string,
+    authenticationData?: Buffer,
+    reasonString?: string,
+    userProperties?: UserProperties,
+  }
+}
+
+export interface IConnectPacket extends IPacket {
+  cmd: 'connect'
+  clientId: string
+  protocolVersion?: 4 | 5 | 3
+  protocolId?: 'MQTT' | 'MQIsdp'
+  clean?: boolean
+  keepalive?: number
+  username?: string
+  password?: Buffer
+  will?: {
+    topic: string
+    payload: Buffer
+    qos?: QoS
+    retain?: boolean
+    properties?: {
+      willDelayInterval?: number,
+      payloadFormatIndicator?: number,
+      messageExpiryInterval?: number,
+      contentType?: string,
+      responseTopic?: string,
+      correlationData?: Buffer,
+      userProperties?: UserProperties
+    }
+  }
+  properties?: {
+    sessionExpiryInterval?: number,
+    receiveMaximum?: number,
+    maximumPacketSize?: number,
+    topicAliasMaximum?: number,
+    requestResponseInformation?: boolean,
+    requestProblemInformation?: boolean,
+    userProperties?: UserProperties,
+    authenticationMethod?: string,
+    authenticationData?: Buffer
+  }
+}
+
+export interface IPublishPacket extends IPacket {
+  cmd: 'publish'
+  qos: QoS
+  dup: boolean
+  retain: boolean
+  topic: string
+  payload: string | Buffer
+  properties?: {
+    payloadFormatIndicator?: boolean,
+    messageExpiryInterval?: number,
+    topicAlias?: number,
+    responseTopic?: string,
+    correlationData?: Buffer,
+    userProperties?: UserProperties,
+    subscriptionIdentifier?: number,
+    contentType?: string
+  }
+}
+
+export interface IConnackPacket extends IPacket {
+  cmd: 'connack'
+  returnCode?: number,
+  reasonCode?: number,
+  sessionPresent: boolean
+  properties?: {
+    sessionExpiryInterval?: number,
+    receiveMaximum?: number,
+    maximumQoS?: number,
+    retainAvailable?: boolean,
+    maximumPacketSize?: number,
+    assignedClientIdentifier?: string,
+    topicAliasMaximum?: number,
+    reasonString?: string,
+    userProperties?: UserProperties,
+    wildcardSubscriptionAvailable?: boolean,
+    subscriptionIdentifiersAvailable?: boolean,
+    sharedSubscriptionAvailable?: boolean,
+    serverKeepAlive?: number,
+    responseInformation?: string,
+    serverReference?: string,
+    authenticationMethod?: string,
+    authenticationData?: Buffer
+  }
+}
+
+export interface ISubscription {
+  topic: string
+  qos: QoS,
+  nl?: boolean,
+  rap?: boolean,
+  rh?: number
+}
+
+export interface ISubscribePacket extends IPacket {
+  cmd: 'subscribe'
+  subscriptions: ISubscription[],
+  properties?: {
+    reasonString?: string,
+    userProperties?: UserProperties
+  }
+}
+
+export interface ISubackPacket extends IPacket {
+  cmd: 'suback',
+  reasonCode?: number,
+  properties?: {
+    reasonString?: string,
+    userProperties?: UserProperties
+  },
+  granted: number[] | Object[]
+}
+
+export interface IUnsubscribePacket extends IPacket {
+  cmd: 'unsubscribe',
+  properties?: {
+    reasonString?: string,
+    userProperties?: UserProperties
+  },
+  unsubscriptions: string[]
+}
+
+export interface IUnsubackPacket extends IPacket {
+  cmd: 'unsuback',
+  reasonCode?: number,
+  properties?: {
+    reasonString?: string,
+    userProperties?: UserProperties
+  }
+}
+
+export interface IPubackPacket extends IPacket {
+  cmd: 'puback',
+  reasonCode?: number,
+  properties?: {
+    reasonString?: string,
+    userProperties?: UserProperties
+  }
+}
+
+export interface IPubcompPacket extends IPacket {
+  cmd: 'pubcomp',
+  reasonCode?: number,
+  properties?: {
+    reasonString?: string,
+    userProperties?: UserProperties
+  }
+}
+
+export interface IPubrelPacket extends IPacket {
+  cmd: 'pubrel',
+  reasonCode?: number,
+  properties?: {
+    reasonString?: string,
+    userProperties?: UserProperties
+  }
+}
+
+export interface IPubrecPacket extends IPacket {
+  cmd: 'pubrec',
+  reasonCode?: number,
+  properties?: {
+    reasonString?: string,
+    userProperties?: UserProperties
+  }
+}
+
+export interface IPingreqPacket extends IPacket {
+  cmd: 'pingreq'
+}
+
+export interface IPingrespPacket extends IPacket {
+  cmd: 'pingresp'
+}
+
+export interface IDisconnectPacket extends IPacket {
+  cmd: 'disconnect',
+  reasonCode?: number,
+  properties?: {
+    sessionExpiryInterval?: number,
+    reasonString?: string,
+    userProperties?: UserProperties,
+    serverReference?: string
+  }
+}
+
+export declare type Packet = IConnectPacket |
+  IPublishPacket |
+  IConnackPacket |
+  ISubscribePacket |
+  ISubackPacket |
+  IUnsubscribePacket |
+  IUnsubackPacket |
+  IPubackPacket |
+  IPubcompPacket |
+  IPubrelPacket |
+  IPingreqPacket |
+  IPingrespPacket |
+  IDisconnectPacket |
+  IPubrecPacket |
+  IAuthPacket
+
+export interface Parser extends EventEmitter {
+  on(event: 'packet', callback: (packet: Packet) => void): this
+
+  on(event: 'error', callback: (error: any) => void): this
+
+  parse(buffer: Buffer, opts?: Object): number
+}
+
+export declare function parser(opts?: Object): Parser
+
+export declare function generate(packet: Packet, opts?: Object): Buffer
+
+export declare function writeToStream(object: Packet, stream: WritableStream, opts?: Object): void
+
+export declare namespace writeToStream {
+  let cacheNumbers: boolean
+}

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1117 - 0
uni_modules/mqtt-packet/writeToStream.js


+ 27 - 0
uni_modules/mqtt/CONTRIBUTING.md

@@ -0,0 +1,27 @@
+# MQTT.js is an OPEN Open Source Project
+
+-----------------------------------------
+
+## What?
+
+Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project.
+
+## Rules
+
+There are a few basic ground-rules for contributors:
+
+1. **No `--force` pushes** or modifying the Git history in any way.
+1. **Non-master branches** ought to be used for ongoing work.
+1. **External API changes and significant modifications** ought to be subject to an **internal pull-request** to solicit feedback from other contributors.
+1. Internal pull-requests to solicit feedback are *encouraged* for any other non-trivial contribution but left to the discretion of the contributor.
+1. Contributors should attempt to adhere to the prevailing code-style.
+
+## Releases
+
+Declaring formal releases remains the prerogative of the project maintainer.
+
+## Changes to this arrangement
+
+This is an experiment and feedback is welcome! This document may also be subject to pull-requests or changes by contributors where you believe you have something valuable to add or change.
+
+-----------------------------------------

+ 15 - 0
uni_modules/mqtt/LICENSE.md

@@ -0,0 +1,15 @@
+The MIT License (MIT)
+=====================
+
+Copyright (c) 2015-2016 MQTT.js contributors
+---------------------------------------
+
+*MQTT.js contributors listed at <https://github.com/mqttjs/MQTT.js#contributors>*
+
+Copyright 2011-2014 by Adam Rudd
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 689 - 0
uni_modules/mqtt/README.md

@@ -0,0 +1,689 @@
+![mqtt.js](https://raw.githubusercontent.com/mqttjs/MQTT.js/137ee0e3940c1f01049a30248c70f24dc6e6f829/MQTT.js.png)
+=======
+
+[![Build Status](https://travis-ci.org/mqttjs/MQTT.js.svg)](https://travis-ci.org/mqttjs/MQTT.js) [![codecov](https://codecov.io/gh/mqttjs/MQTT.js/branch/master/graph/badge.svg)](https://codecov.io/gh/mqttjs/MQTT.js)
+
+[![NPM](https://nodei.co/npm-dl/mqtt.png)](https://nodei.co/npm/mqtt/) [![NPM](https://nodei.co/npm/mqtt.png)](https://nodei.co/npm/mqtt/)
+
+[![Sauce Test Status](https://saucelabs.com/browser-matrix/mqttjs.svg)](https://saucelabs.com/u/mqttjs)
+
+MQTT.js is a client library for the [MQTT](http://mqtt.org/) protocol, written
+in JavaScript for node.js and the browser.
+
+* [Upgrade notes](#notes)
+* [Installation](#install)
+* [Example](#example)
+* [Command Line Tools](#cli)
+* [API](#api)
+* [Browser](#browser)
+* [Weapp](#weapp)
+* [About QoS](#qos)
+* [TypeScript](#typescript)
+* [Contributing](#contributing)
+* [License](#license)
+
+MQTT.js is an OPEN Open Source Project, see the [Contributing](#contributing) section to find out what this means.
+
+[![JavaScript Style
+Guide](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard)
+
+
+<a name="notes"></a>
+## Important notes for existing users
+
+v2.0.0 removes support for node v0.8, v0.10 and v0.12, and it is 3x faster in sending
+packets. It also removes all the deprecated functionality in v1.0.0,
+mainly `mqtt.createConnection` and `mqtt.Server`. From v2.0.0,
+subscriptions are restored upon reconnection if `clean: true`.
+v1.x.x is now in *LTS*, and it will keep being supported as long as
+there are v0.8, v0.10 and v0.12 users.
+
+v1.0.0 improves the overall architecture of the project, which is now
+split into three components: MQTT.js keeps the Client,
+[mqtt-connection](http://npm.im/mqtt-connection) includes the barebone
+Connection code for server-side usage, and [mqtt-packet](http://npm.im/mqtt-packet)
+includes the protocol parser and generator. The new Client improves
+performance by a 30% factor, embeds Websocket support
+([MOWS](http://npm.im/mows) is now deprecated), and it has a better
+support for QoS 1 and 2. The previous API is still supported but
+deprecated, as such, it is not documented in this README.
+
+As a __breaking change__, the `encoding` option in the old client is
+removed, and now everything is UTF-8 with the exception of the
+`password` in the CONNECT message and `payload` in the PUBLISH message,
+which are `Buffer`.
+
+Another __breaking change__ is that MQTT.js now defaults to MQTT v3.1.1,
+so to support old brokers, please read the [client options doc](#client).
+
+MQTT v5 support is experimental as it has not been implemented by brokers yet.
+
+<a name="install"></a>
+## Installation
+
+```sh
+npm install mqtt --save
+```
+
+<a name="example"></a>
+## Example
+
+For the sake of simplicity, let's put the subscriber and the publisher in the same file:
+
+```js
+var mqtt = require('mqtt')
+var client  = mqtt.connect('mqtt://test.mosquitto.org')
+
+client.on('connect', function () {
+  client.subscribe('presence', function (err) {
+    if (!err) {
+      client.publish('presence', 'Hello mqtt')
+    }
+  })
+})
+
+client.on('message', function (topic, message) {
+  // message is Buffer
+  console.log(message.toString())
+  client.end()
+})
+```
+
+output:
+```
+Hello mqtt
+```
+
+If you want to run your own MQTT broker, you can use
+[Mosquitto](http://mosquitto.org) or
+[Mosca](http://mcollina.github.io/mosca/), and launch it.
+You can also use a test instance: test.mosquitto.org and test.mosca.io
+are both public.
+
+If you do not want to install a separate broker, you can try using the
+[mqtt-connection](https://www.npmjs.com/package/mqtt-connection).
+
+to use MQTT.js in the browser see the [browserify](#browserify) section
+
+<a name="promises"></a>
+## Promise support
+
+If you want to use the new [async-await](https://blog.risingstack.com/async-await-node-js-7-nightly/) functionality in JavaScript, or just prefer using Promises instead of callbacks, [async-mqtt](https://github.com/mqttjs/async-mqtt) is a wrapper over MQTT.js which uses promises instead of callbacks when possible.
+
+<a name="cli"></a>
+## Command Line Tools
+
+MQTT.js bundles a command to interact with a broker.
+In order to have it available on your path, you should install MQTT.js
+globally:
+
+```sh
+npm install mqtt -g
+```
+
+Then, on one terminal
+
+```
+mqtt sub -t 'hello' -h 'test.mosquitto.org' -v
+```
+
+On another
+
+```
+mqtt pub -t 'hello' -h 'test.mosquitto.org' -m 'from MQTT.js'
+```
+
+See `mqtt help <command>` for the command help.
+
+<a name="api"></a>
+## API
+
+  * <a href="#connect"><code>mqtt.<b>connect()</b></code></a>
+  * <a href="#client"><code>mqtt.<b>Client()</b></code></a>
+  * <a href="#publish"><code>mqtt.Client#<b>publish()</b></code></a>
+  * <a href="#subscribe"><code>mqtt.Client#<b>subscribe()</b></code></a>
+  * <a href="#unsubscribe"><code>mqtt.Client#<b>unsubscribe()</b></code></a>
+  * <a href="#end"><code>mqtt.Client#<b>end()</b></code></a>
+  * <a href="#removeOutgoingMessage"><code>mqtt.Client#<b>removeOutgoingMessage()</b></code></a>
+  * <a href="#reconnect"><code>mqtt.Client#<b>reconnect()</b></code></a>
+  * <a href="#handleMessage"><code>mqtt.Client#<b>handleMessage()</b></code></a>
+  * <a href="#connected"><code>mqtt.Client#<b>connected</b></code></a>
+  * <a href="#reconnecting"><code>mqtt.Client#<b>reconnecting</b></code></a>
+  * <a href="#getLastMessageId"><code>mqtt.Client#<b>getLastMessageId()</b></code></a>
+  * <a href="#store"><code>mqtt.<b>Store()</b></code></a>
+  * <a href="#put"><code>mqtt.Store#<b>put()</b></code></a>
+  * <a href="#del"><code>mqtt.Store#<b>del()</b></code></a>
+  * <a href="#createStream"><code>mqtt.Store#<b>createStream()</b></code></a>
+  * <a href="#close"><code>mqtt.Store#<b>close()</b></code></a>
+
+-------------------------------------------------------
+<a name="connect"></a>
+### mqtt.connect([url], options)
+
+Connects to the broker specified by the given url and options and
+returns a [Client](#client).
+
+The URL can be on the following protocols: 'mqtt', 'mqtts', 'tcp',
+'tls', 'ws', 'wss'. The URL can also be an object as returned by
+[`URL.parse()`](http://nodejs.org/api/url.html#url_url_parse_urlstr_parsequerystring_slashesdenotehost),
+in that case the two objects are merged, i.e. you can pass a single
+object with both the URL and the connect options.
+
+You can also specify a `servers` options with content: `[{ host:
+'localhost', port: 1883 }, ... ]`, in that case that array is iterated
+at every connect.
+
+For all MQTT-related options, see the [Client](#client)
+constructor.
+
+-------------------------------------------------------
+<a name="client"></a>
+### mqtt.Client(streamBuilder, options)
+
+The `Client` class wraps a client connection to an
+MQTT broker over an arbitrary transport method (TCP, TLS,
+WebSocket, ecc).
+
+`Client` automatically handles the following:
+
+* Regular server pings
+* QoS flow
+* Automatic reconnections
+* Start publishing before being connected
+
+The arguments are:
+
+* `streamBuilder` is a function that returns a subclass of the `Stream` class that supports
+the `connect` event. Typically a `net.Socket`.
+* `options` is the client connection options (see: the [connect packet](https://github.com/mcollina/mqtt-packet#connect)). Defaults:
+  * `wsOptions`: is the WebSocket connection options. Default is `{}`.
+     It's specific for WebSockets. For possible options have a look at: https://github.com/websockets/ws/blob/master/doc/ws.md.
+  * `keepalive`: `60` seconds, set to `0` to disable
+  * `reschedulePings`: reschedule ping messages after sending packets (default `true`)
+  * `clientId`: `'mqttjs_' + Math.random().toString(16).substr(2, 8)`
+  * `protocolId`: `'MQTT'`
+  * `protocolVersion`: `4`
+  * `clean`: `true`, set to false to receive QoS 1 and 2 messages while
+    offline
+  * `reconnectPeriod`: `1000` milliseconds, interval between two
+    reconnections
+  * `connectTimeout`: `30 * 1000` milliseconds, time to wait before a
+    CONNACK is received
+  * `username`: the username required by your broker, if any
+  * `password`: the password required by your broker, if any
+  * `incomingStore`: a [Store](#store) for the incoming packets
+  * `outgoingStore`: a [Store](#store) for the outgoing packets
+  * `queueQoSZero`: if connection is broken, queue outgoing QoS zero messages (default `true`)
+  * `customHandleAcks`: MQTT 5 feature of custom handling puback and pubrec packets. Its callback:
+      ```js
+        customHandleAcks: function(topic, message, packet, done) {*some logic wit colling done(error, reasonCode)*}
+      ```
+  * `properties`: properties MQTT 5.0.
+  `object` that supports the following properties:
+    * `sessionExpiryInterval`: representing the Session Expiry Interval in seconds `number`,
+    * `receiveMaximum`: representing the Receive Maximum value `number`,
+    * `maximumPacketSize`: representing the Maximum Packet Size the Client is willing to accept `number`,
+    * `topicAliasMaximum`: representing the Topic Alias Maximum value indicates the highest value that the Client will accept as a Topic Alias sent by the Server `number`,
+    * `requestResponseInformation`: The Client uses this value to request the Server to return Response Information in the CONNACK `boolean`,
+    * `requestProblemInformation`: The Client uses this value to indicate whether the Reason String or User Properties are sent in the case of failures `boolean`,
+    * `userProperties`: The User Property is allowed to appear multiple times to represent multiple name, value pairs `object`,
+    * `authenticationMethod`: the name of the authentication method used for extended authentication `string`,
+    * `authenticationData`: Binary Data containing authentication data `binary`
+  * `authPacket`: settings for auth packet `object`
+  * `will`: a message that will sent by the broker automatically when
+     the client disconnect badly. The format is:
+    * `topic`: the topic to publish
+    * `payload`: the message to publish
+    * `qos`: the QoS
+    * `retain`: the retain flag
+    * `properties`: properties of will by MQTT 5.0:
+      * `willDelayInterval`: representing the Will Delay Interval in seconds `number`,
+      * `payloadFormatIndicator`: Will Message is UTF-8 Encoded Character Data or not `boolean`,
+      * `messageExpiryInterval`: value is the lifetime of the Will Message in seconds and is sent as the Publication Expiry Interval when the Server publishes the Will Message `number`,
+      * `contentType`: describing the content of the Will Message `string`,
+      * `responseTopic`: String which is used as the Topic Name for a response message `string`,
+      * `correlationData`: The Correlation Data is used by the sender of the Request Message to identify which request the Response Message is for when it is received `binary`,
+      * `userProperties`: The User Property is allowed to appear multiple times to represent multiple name, value pairs `object`
+  * `transformWsUrl` : optional `(url, options, client) => url` function
+        For ws/wss protocols only. Can be used to implement signing
+        urls which upon reconnect can have become expired.
+  * `resubscribe` : if connection is broken and reconnects,
+     subscribed topics are automatically subscribed again (default `true`)
+
+In case mqtts (mqtt over tls) is required, the `options` object is
+passed through to
+[`tls.connect()`](http://nodejs.org/api/tls.html#tls_tls_connect_options_callback).
+If you are using a **self-signed certificate**, pass the `rejectUnauthorized: false` option.
+Beware that you are exposing yourself to man in the middle attacks, so it is a configuration
+that is not recommended for production environments.
+
+If you are connecting to a broker that supports only MQTT 3.1 (not
+3.1.1 compliant), you should pass these additional options:
+
+```js
+{
+  protocolId: 'MQIsdp',
+  protocolVersion: 3
+}
+```
+
+This is confirmed on RabbitMQ 3.2.4, and on Mosquitto < 1.3. Mosquitto
+version 1.3 and 1.4 works fine without those.
+
+#### Event `'connect'`
+
+`function (connack) {}`
+
+Emitted on successful (re)connection (i.e. connack rc=0).
+* `connack` received connack packet. When `clean` connection option is `false` and server has a previous session
+for `clientId` connection option, then `connack.sessionPresent` flag is `true`. When that is the case,
+you may rely on stored session and prefer not to send subscribe commands for the client.
+
+#### Event `'reconnect'`
+
+`function () {}`
+
+Emitted when a reconnect starts.
+
+#### Event `'close'`
+
+`function () {}`
+
+Emitted after a disconnection.
+
+#### Event `'disconnect'`
+
+`function (packet) {}`
+
+Emitted after receiving disconnect packet from broker. MQTT 5.0 feature.
+
+#### Event `'offline'`
+
+`function () {}`
+
+Emitted when the client goes offline.
+
+#### Event `'error'`
+
+`function (error) {}`
+
+Emitted when the client cannot connect (i.e. connack rc != 0) or when a
+parsing error occurs.
+
+#### Event `'end'`
+
+`function () {}`
+
+Emitted when <a href="#end"><code>mqtt.Client#<b>end()</b></code></a> is called.
+If a callback was passed to `mqtt.Client#end()`, this event is emitted once the
+callback returns.
+
+#### Event `'message'`
+
+`function (topic, message, packet) {}`
+
+Emitted when the client receives a publish packet
+* `topic` topic of the received packet
+* `message` payload of the received packet
+* `packet` received packet, as defined in
+  [mqtt-packet](https://github.com/mcollina/mqtt-packet#publish)
+
+#### Event `'packetsend'`
+
+`function (packet) {}`
+
+Emitted when the client sends any packet. This includes .published() packets
+as well as packets used by MQTT for managing subscriptions and connections
+* `packet` received packet, as defined in
+  [mqtt-packet](https://github.com/mcollina/mqtt-packet)
+
+#### Event `'packetreceive'`
+
+`function (packet) {}`
+
+Emitted when the client receives any packet. This includes packets from
+subscribed topics as well as packets used by MQTT for managing subscriptions
+and connections
+* `packet` received packet, as defined in
+  [mqtt-packet](https://github.com/mcollina/mqtt-packet)
+
+-------------------------------------------------------
+<a name="publish"></a>
+### mqtt.Client#publish(topic, message, [options], [callback])
+
+Publish a message to a topic
+
+* `topic` is the topic to publish to, `String`
+* `message` is the message to publish, `Buffer` or `String`
+* `options` is the options to publish with, including:
+  * `qos` QoS level, `Number`, default `0`
+  * `retain` retain flag, `Boolean`, default `false`
+  * `dup` mark as duplicate flag, `Boolean`, default `false`
+  * `properties`: MQTT 5.0 properties `object`
+    * `payloadFormatIndicator`: Payload is UTF-8 Encoded Character Data or not `boolean`,
+    * `messageExpiryInterval`: the lifetime of the Application Message in seconds `number`,
+    * `topicAlias`: value that is used to identify the Topic instead of using the Topic Name `number`,
+    * `responseTopic`: String which is used as the Topic Name for a response message `string`,
+    * `correlationData`: used by the sender of the Request Message to identify which request the Response Message is for when it is received `binary`,
+    * `userProperties`: The User Property is allowed to appear multiple times to represent multiple name, value pairs `object`,
+    * `subscriptionIdentifier`: representing the identifier of the subscription `number`,
+    * `contentType`: String describing the content of the Application Message `string`
+  * `cbStorePut` - `function ()`, fired when message is put into `outgoingStore` if QoS is `1` or `2`.
+* `callback` - `function (err)`, fired when the QoS handling completes,
+  or at the next tick if QoS 0. An error occurs if client is disconnecting.
+
+-------------------------------------------------------
+<a name="subscribe"></a>
+### mqtt.Client#subscribe(topic/topic array/topic object, [options], [callback])
+
+Subscribe to a topic or topics
+
+* `topic` is a `String` topic to subscribe to or an `Array` of
+  topics to subscribe to. It can also be an object, it has as object
+  keys the topic name and as value the QoS, like `{'test1': {qos: 0}, 'test2': {qos: 1}}`.
+  MQTT `topic` wildcard characters are supported (`+` - for single level and `#` - for multi level)
+* `options` is the options to subscribe with, including:
+  * `qos` qos subscription level, default 0
+  * `nl` No Local MQTT 5.0 flag (If the value is true, Application Messages MUST NOT be forwarded to a connection with a ClientID equal to the ClientID of the publishing connection)
+  * `rap` Retain as Published MQTT 5.0 flag (If true, Application Messages forwarded using this subscription keep the RETAIN flag they were published with. If false, Application Messages forwarded using this subscription have the RETAIN flag set to 0.)
+  * `rh` Retain Handling MQTT 5.0 (This option specifies whether retained messages are sent when the subscription is established.)
+  * `properties`: `object`
+    * `subscriptionIdentifier`:  representing the identifier of the subscription `number`,
+    * `userProperties`: The User Property is allowed to appear multiple times to represent multiple name, value pairs `object`
+* `callback` - `function (err, granted)`
+  callback fired on suback where:
+  * `err` a subscription error or an error that occurs when client is disconnecting
+  * `granted` is an array of `{topic, qos}` where:
+    * `topic` is a subscribed to topic
+    * `qos` is the granted qos level on it
+
+-------------------------------------------------------
+<a name="unsubscribe"></a>
+### mqtt.Client#unsubscribe(topic/topic array, [options], [callback])
+
+Unsubscribe from a topic or topics
+
+* `topic` is a `String` topic or an array of topics to unsubscribe from
+* `options`: options of unsubscribe.
+  * `properties`: `object`
+      * `userProperties`: The User Property is allowed to appear multiple times to represent multiple name, value pairs `object`
+* `callback` - `function (err)`, fired on unsuback. An error occurs if client is disconnecting.
+
+-------------------------------------------------------
+<a name="end"></a>
+### mqtt.Client#end([force], [options], [cb])
+
+Close the client, accepts the following options:
+
+* `force`: passing it to true will close the client right away, without
+  waiting for the in-flight messages to be acked. This parameter is
+  optional.
+* `options`: options of disconnect.
+  * `reasonCode`: Disconnect Reason Code `number`
+  * `properties`: `object`
+    * `sessionExpiryInterval`: representing the Session Expiry Interval in seconds `number`,
+    * `reasonString`: representing the reason for the disconnect `string`,
+    * `userProperties`: The User Property is allowed to appear multiple times to represent multiple name, value pairs `object`,
+    * `serverReference`: String which can be used by the Client to identify another Server to use `string`
+* `cb`: will be called when the client is closed. This parameter is
+  optional.
+
+-------------------------------------------------------
+<a name="removeOutgoingMessage"></a>
+### mqtt.Client#removeOutgoingMessage(mid)
+
+Remove a message from the outgoingStore.
+The outgoing callback will be called with Error('Message removed') if the message is removed.
+
+After this function is called, the messageId is released and becomes reusable.
+
+* `mid`: The messageId of the message in the outgoingStore.
+
+-------------------------------------------------------
+<a name="reconnect"></a>
+### mqtt.Client#reconnect()
+
+Connect again using the same options as connect()
+
+-------------------------------------------------------
+<a name="handleMessage"></a>
+### mqtt.Client#handleMessage(packet, callback)
+
+Handle messages with backpressure support, one at a time.
+Override at will, but __always call `callback`__, or the client
+will hang.
+
+-------------------------------------------------------
+<a name="connected"></a>
+### mqtt.Client#connected
+
+Boolean : set to `true` if the client is connected. `false` otherwise.
+
+-------------------------------------------------------
+<a name="getLastMessageId"></a>
+### mqtt.Client#getLastMessageId()
+
+Number : get last message id. This is for sent messages only.
+
+-------------------------------------------------------
+<a name="reconnecting"></a>
+### mqtt.Client#reconnecting
+
+Boolean : set to `true` if the client is trying to reconnect to the server. `false` otherwise.
+
+-------------------------------------------------------
+<a name="store"></a>
+### mqtt.Store(options)
+
+In-memory implementation of the message store.
+
+* `options` is the store options:
+  * `clean`: `true`, clean inflight messages when close is called (default `true`)
+
+Other implementations of `mqtt.Store`:
+
+* [mqtt-level-store](http://npm.im/mqtt-level-store) which uses
+  [Level-browserify](http://npm.im/level-browserify) to store the inflight
+  data, making it usable both in Node and the Browser.
+* [mqtt-nedbb-store](https://github.com/behrad/mqtt-nedb-store) which
+  uses [nedb](https://www.npmjs.com/package/nedb) to store the inflight
+  data.
+* [mqtt-localforage-store](http://npm.im/mqtt-localforage-store) which uses
+  [localForage](http://npm.im/localforage) to store the inflight
+  data, making it usable in the Browser without browserify.
+
+-------------------------------------------------------
+<a name="put"></a>
+### mqtt.Store#put(packet, callback)
+
+Adds a packet to the store, a packet is
+anything that has a `messageId` property.
+The callback is called when the packet has been stored.
+
+-------------------------------------------------------
+<a name="createStream"></a>
+### mqtt.Store#createStream()
+
+Creates a stream with all the packets in the store.
+
+-------------------------------------------------------
+<a name="del"></a>
+### mqtt.Store#del(packet, cb)
+
+Removes a packet from the store, a packet is
+anything that has a `messageId` property.
+The callback is called when the packet has been removed.
+
+-------------------------------------------------------
+<a name="close"></a>
+### mqtt.Store#close(cb)
+
+Closes the Store.
+
+<a name="browser"></a>
+## Browser
+
+<a name="cdn"></a>
+### Via CDN
+
+The MQTT.js bundle is available through http://unpkg.com, specifically
+at https://unpkg.com/mqtt/dist/mqtt.min.js.
+See http://unpkg.com for the full documentation on version ranges.
+
+<a name="weapp"></a>
+## WeChat Mini Program
+Support [WeChat Mini Program](https://mp.weixin.qq.com/). See [Doc](https://mp.weixin.qq.com/debug/wxadoc/dev/api/network-socket.html).
+<a name="example"></a>
+
+## Example(js)
+
+```js
+var mqtt = require('mqtt')
+var client = mqtt.connect('wxs://test.mosquitto.org')
+```
+
+## Example(ts)
+
+```ts
+import { connect } from 'mqtt';
+const client = connect('wxs://test.mosquitto.org');
+```
+
+## Ali Mini Program
+Surport [Ali Mini Program](https://open.alipay.com/channel/miniIndex.htm). See [Doc](https://docs.alipay.com/mini/developer/getting-started).
+<a name="example"></a>
+
+## Example(js)
+
+```js
+var mqtt = require('mqtt')
+var client = mqtt.connect('alis://test.mosquitto.org')
+```
+
+## Example(ts)
+
+```ts
+import { connect } from 'mqtt';
+const client  = connect('alis://test.mosquitto.org');
+```
+
+<a name="browserify"></a>
+### Browserify
+
+In order to use MQTT.js as a browserify module you can either require it in your browserify bundles or build it as a stand alone module. The exported module is AMD/CommonJs compatible and it will add an object in the global space.
+
+```javascript
+npm install -g browserify // install browserify
+cd node_modules/mqtt
+npm install . // install dev dependencies
+browserify mqtt.js -s mqtt > browserMqtt.js // require mqtt in your client-side app
+```
+
+<a name="webpack"></a>
+### Webpack
+
+Just like browserify, export MQTT.js as library. The exported module would be `var mqtt = xxx` and it will add an object in the global space. You could also export module in other [formats (AMD/CommonJS/others)](http://webpack.github.io/docs/configuration.html#output-librarytarget) by setting **output.libraryTarget** in webpack configuration.
+
+```javascript
+npm install -g webpack // install webpack
+
+cd node_modules/mqtt
+npm install . // install dev dependencies
+webpack mqtt.js ./browserMqtt.js --output-library mqtt
+```
+
+you can then use mqtt.js in the browser with the same api than node's one.
+
+```html
+<html>
+<head>
+  <title>test Ws mqtt.js</title>
+</head>
+<body>
+<script src="./browserMqtt.js"></script>
+<script>
+  var client = mqtt.connect() // you add a ws:// url here
+  client.subscribe("mqtt/demo")
+
+  client.on("message", function (topic, payload) {
+    alert([topic, payload].join(": "))
+    client.end()
+  })
+
+  client.publish("mqtt/demo", "hello world!")
+</script>
+</body>
+</html>
+```
+
+Your broker should accept websocket connection (see [MQTT over Websockets](https://github.com/mcollina/mosca/wiki/MQTT-over-Websockets) to setup [Mosca](http://mcollina.github.io/mosca/)).
+
+<a name="signedurls"></a>
+### Signed WebSocket Urls
+
+If you need to sign an url, for example for [AWS IoT](http://docs.aws.amazon.com/iot/latest/developerguide/protocols.html#mqtt-ws),
+then you can pass in a `transformWsUrl` function to the <a href="#connect"><code>mqtt.<b>connect()</b></code></a> options
+This is needed because signed urls have an expiry and eventually upon reconnects, a new signed url needs to be created:
+
+```js
+// This module doesn't actually exist, just an example
+var awsIotUrlSigner = require('awsIotUrlSigner')
+mqtt.connect('wss://a2ukbzaqo9vbpb.iot.ap-southeast-1.amazonaws.com/mqtt', {
+  transformWsUrl: function (url, options, client) {
+    // It's possible to inspect some state on options(pre parsed url components)
+    // and the client (reconnect state etc)
+    return awsIotUrlSigner(url)
+  }
+})
+
+// Now every time a new WebSocket connection is opened (hopefully not that
+// often) we get a freshly signed url
+
+```
+
+<a name="qos"></a>
+## About QoS
+
+Here is how QoS works:
+
+* QoS 0 : received **at most once** : The packet is sent, and that's it. There is no validation about whether it has been received.
+* QoS 1 : received **at least once** : The packet is sent and stored as long as the client has not received a confirmation from the server. MQTT ensures that it *will* be received, but there can be duplicates.
+* QoS 2 : received **exactly once** : Same as QoS 1 but there is no duplicates.
+
+About data consumption, obviously, QoS 2 > QoS 1 > QoS 0, if that's a concern to you.
+
+<a name="typescript"></a>
+## Usage with TypeScript
+This repo bundles TypeScript definition files for use in TypeScript projects and to support tools that can read `.d.ts` files.
+
+### Pre-requisites
+Before you can begin using these TypeScript definitions with your project, you need to make sure your project meets a few of these requirements:
+ * TypeScript >= 2.1
+ * Set tsconfig.json: `{"compilerOptions" : {"moduleResolution" : "node"}, ...}`
+ * Includes the TypeScript definitions for node. You can use npm to install this by typing the following into a terminal window:
+   `npm install --save-dev @types/node`
+
+<a name="contributing"></a>
+## Contributing
+
+MQTT.js is an **OPEN Open Source Project**. This means that:
+
+> Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project.
+
+See the [CONTRIBUTING.md](https://github.com/mqttjs/MQTT.js/blob/master/CONTRIBUTING.md) file for more details.
+
+### Contributors
+
+MQTT.js is only possible due to the excellent work of the following contributors:
+
+<table><tbody>
+<tr><th align="left">Adam Rudd</th><td><a href="https://github.com/adamvr">GitHub/adamvr</a></td><td><a href="http://twitter.com/adam_vr">Twitter/@adam_vr</a></td></tr>
+<tr><th align="left">Matteo Collina</th><td><a href="https://github.com/mcollina">GitHub/mcollina</a></td><td><a href="http://twitter.com/matteocollina">Twitter/@matteocollina</a></td></tr>
+<tr><th align="left">Maxime Agor</th><td><a href="https://github.com/4rzael">GitHub/4rzael</a></td><td><a href="http://twitter.com/4rzael">Twitter/@4rzael</a></td></tr>
+<tr><th align="left">Siarhei Buntsevich</th><td><a href="https://github.com/scarry1992">GitHub/scarry1992</a></td></tr>
+</tbody></table>
+
+<a name="license"></a>
+## License
+
+MIT

+ 146 - 0
uni_modules/mqtt/bin/pub.js

@@ -0,0 +1,146 @@
+#!/usr/bin/env node
+
+'use strict'
+
+var mqtt = require('../')
+var pump = require('pump')
+var path = require('path')
+var fs = require('fs')
+var concat = require('concat-stream')
+var Writable = require('readable-stream').Writable
+var helpMe = require('help-me')({
+  dir: path.join(__dirname, '..', 'doc')
+})
+var minimist = require('minimist')
+var split2 = require('split2')
+
+function send (args) {
+  var client = mqtt.connect(args)
+  client.on('connect', function () {
+    client.publish(args.topic, args.message, args, function (err) {
+      if (err) {
+        console.warn(err)
+      }
+      client.end()
+    })
+  })
+  client.on('error', function (err) {
+    console.warn(err)
+    client.end()
+  })
+}
+
+function multisend (args) {
+  var client = mqtt.connect(args)
+  var sender = new Writable({
+    objectMode: true
+  })
+  sender._write = function (line, enc, cb) {
+    client.publish(args.topic, line.trim(), args, cb)
+  }
+
+  client.on('connect', function () {
+    pump(process.stdin, split2(), sender, function (err) {
+      client.end()
+      if (err) {
+        throw err
+      }
+    })
+  })
+}
+
+function start (args) {
+  args = minimist(args, {
+    string: ['hostname', 'username', 'password', 'key', 'cert', 'ca', 'message', 'clientId', 'i', 'id'],
+    boolean: ['stdin', 'retain', 'help', 'insecure', 'multiline'],
+    alias: {
+      port: 'p',
+      hostname: ['h', 'host'],
+      topic: 't',
+      message: 'm',
+      qos: 'q',
+      clientId: ['i', 'id'],
+      retain: 'r',
+      username: 'u',
+      password: 'P',
+      stdin: 's',
+      multiline: 'M',
+      protocol: ['C', 'l'],
+      help: 'H',
+      ca: 'cafile'
+    },
+    default: {
+      host: 'localhost',
+      qos: 0,
+      retain: false,
+      topic: '',
+      message: ''
+    }
+  })
+
+  if (args.help) {
+    return helpMe.toStdout('publish')
+  }
+
+  if (args.key) {
+    args.key = fs.readFileSync(args.key)
+  }
+
+  if (args.cert) {
+    args.cert = fs.readFileSync(args.cert)
+  }
+
+  if (args.ca) {
+    args.ca = fs.readFileSync(args.ca)
+  }
+
+  if (args.key && args.cert && !args.protocol) {
+    args.protocol = 'mqtts'
+  }
+
+  if (args.port) {
+    if (typeof args.port !== 'number') {
+      console.warn('# Port: number expected, \'%s\' was given.', typeof args.port)
+      return
+    }
+  }
+
+  if (args['will-topic']) {
+    args.will = {}
+    args.will.topic = args['will-topic']
+    args.will.payload = args['will-message']
+    args.will.qos = args['will-qos']
+    args.will.retain = args['will-retain']
+  }
+
+  if (args.insecure) {
+    args.rejectUnauthorized = false
+  }
+
+  args.topic = (args.topic || args._.shift()).toString()
+  args.message = (args.message || args._.shift()).toString()
+
+  if (!args.topic) {
+    console.error('missing topic\n')
+    return helpMe.toStdout('publish')
+  }
+
+  if (args.stdin) {
+    if (args.multiline) {
+      multisend(args)
+    } else {
+      process.stdin.pipe(concat(function (data) {
+        args.message = data
+        send(args)
+      }))
+    }
+  } else {
+    send(args)
+  }
+}
+
+module.exports = start
+
+if (require.main === module) {
+  start(process.argv.slice(2))
+}

+ 123 - 0
uni_modules/mqtt/bin/sub.js

@@ -0,0 +1,123 @@
+#!/usr/bin/env node
+
+var mqtt = require('../')
+var path = require('path')
+var fs = require('fs')
+var helpMe = require('help-me')({
+  dir: path.join(__dirname, '..', 'doc')
+})
+var minimist = require('minimist')
+
+function start (args) {
+  args = minimist(args, {
+    string: ['hostname', 'username', 'password', 'key', 'cert', 'ca', 'clientId', 'i', 'id'],
+    boolean: ['stdin', 'help', 'clean', 'insecure'],
+    alias: {
+      port: 'p',
+      hostname: ['h', 'host'],
+      topic: 't',
+      qos: 'q',
+      clean: 'c',
+      keepalive: 'k',
+      clientId: ['i', 'id'],
+      username: 'u',
+      password: 'P',
+      protocol: ['C', 'l'],
+      verbose: 'v',
+      help: '-H',
+      ca: 'cafile'
+    },
+    default: {
+      host: 'localhost',
+      qos: 0,
+      retain: false,
+      clean: true,
+      keepAlive: 30 // 30 sec
+    }
+  })
+
+  if (args.help) {
+    return helpMe.toStdout('subscribe')
+  }
+
+  args.topic = args.topic || args._.shift()
+
+  if (!args.topic) {
+    console.error('missing topic\n')
+    return helpMe.toStdout('subscribe')
+  }
+
+  if (args.key) {
+    args.key = fs.readFileSync(args.key)
+  }
+
+  if (args.cert) {
+    args.cert = fs.readFileSync(args.cert)
+  }
+
+  if (args.ca) {
+    args.ca = fs.readFileSync(args.ca)
+  }
+
+  if (args.key && args.cert && !args.protocol) {
+    args.protocol = 'mqtts'
+  }
+
+  if (args.insecure) {
+    args.rejectUnauthorized = false
+  }
+
+  if (args.port) {
+    if (typeof args.port !== 'number') {
+      console.warn('# Port: number expected, \'%s\' was given.', typeof args.port)
+      return
+    }
+  }
+
+  if (args['will-topic']) {
+    args.will = {}
+    args.will.topic = args['will-topic']
+    args.will.payload = args['will-message']
+    args.will.qos = args['will-qos']
+    args.will.retain = args['will-retain']
+  }
+
+  args.keepAlive = args['keep-alive']
+
+  var client = mqtt.connect(args)
+
+  client.on('connect', function () {
+    client.subscribe(args.topic, { qos: args.qos }, function (err, result) {
+      if (err) {
+        console.error(err)
+        process.exit(1)
+      }
+
+      result.forEach(function (sub) {
+        if (sub.qos > 2) {
+          console.error('subscription negated to', sub.topic, 'with code', sub.qos)
+          process.exit(1)
+        }
+      })
+    })
+  })
+
+  client.on('message', function (topic, payload) {
+    if (args.verbose) {
+      console.log(topic, payload.toString())
+    } else {
+      console.log(payload.toString())
+    }
+  })
+
+  client.on('error', function (err) {
+    console.warn(err)
+    client.end()
+  })
+}
+
+module.exports = start
+
+if (require.main === module) {
+  start(process.argv.slice(2))
+}

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 14633 - 0
uni_modules/mqtt/dist/mqtt.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 0
uni_modules/mqtt/dist/mqtt.min.js


+ 8 - 0
uni_modules/mqtt/doc/help.txt

@@ -0,0 +1,8 @@
+MQTT.js command line interface, available commands are:
+
+  * publish     publish a message to the broker
+  * subscribe   subscribe for updates from the broker
+  * version     the current MQTT.js version
+  * help        help about commands
+
+Launch 'mqtt help [command]' to know more about the commands.

+ 26 - 0
uni_modules/mqtt/doc/publish.txt

@@ -0,0 +1,26 @@
+Usage: mqtt publish [opts] topic [message]
+
+Available options:
+
+  -h/--hostname HOST    the broker host
+  -p/--port PORT        the broker port
+  -i/--client-id ID     the client id
+  -q/--qos 0/1/2        the QoS of the message
+  -t/--topic TOPIC      the message topic
+  -m/--message MSG      the message body
+  -r/--retain           send a retained message
+  -s/--stdin            read the message body from stdin
+  -M/--multiline        read lines from stdin as multiple messages 
+  -u/--username USER    the username
+  -P/--password PASS    the password
+  -C/--protocol PROTO   the protocol to use, 'mqtt',
+                        'mqtts', 'ws' or 'wss'
+  --key PATH            path to the key file
+  --cert PATH           path to the cert file
+  --ca PATH             path to the ca certificate
+  --insecure            do not verify the server certificate
+  --will-topic TOPIC    the will topic
+  --will-payload BODY   the will message
+  --will-qos 0/1/2      the will qos
+  --will-retain         send a will retained message 
+  -H/--help             show this

+ 26 - 0
uni_modules/mqtt/doc/subscribe.txt

@@ -0,0 +1,26 @@
+Usage: mqtt subscribe [opts] [topic]
+
+Available options:
+
+  -h/--hostname HOST    the broker host
+  -p/--port PORT        the broker port
+  -i/--client-id ID     the client id
+  -q/--qos 0/1/2        the QoS of the message
+  --no-clean            do not discard any pending message for
+                        the given id
+  -t/--topic TOPIC      the message topic
+  -k/--keepalive SEC    send a ping every SEC seconds
+  -u/--username USER    the username
+  -P/--password PASS    the password
+  -l/--protocol PROTO   the protocol to use, 'mqtt',
+                        'mqtts', 'ws' or 'wss'
+  --key PATH            path to the key file
+  --cert PATH           path to the cert file
+  --ca PATH             path to the ca certificate
+  --insecure            do not verify the server certificate
+  --will-topic TOPIC    the will topic
+  --will-message BODY   the will message
+  --will-qos 0/1/2      the will qos
+  --will-retain         send a will retained message
+  -v/--verbose          print the topic before the message
+  -H/--help             show this

+ 24 - 0
uni_modules/mqtt/examples/client/secure-client.js

@@ -0,0 +1,24 @@
+'use strict'
+
+var mqtt = require('../..')
+var path = require('path')
+var fs = require('fs')
+var KEY = fs.readFileSync(path.join(__dirname, '..', '..', 'test', 'helpers', 'tls-key.pem'))
+var CERT = fs.readFileSync(path.join(__dirname, '..', '..', 'test', 'helpers', 'tls-cert.pem'))
+
+var PORT = 8443
+
+var options = {
+  port: PORT,
+  key: KEY,
+  cert: CERT,
+  rejectUnauthorized: false
+}
+
+var client = mqtt.connect(options)
+
+client.subscribe('messages')
+client.publish('messages', 'Current time is: ' + new Date())
+client.on('message', function (topic, message) {
+  console.log(message)
+})

+ 13 - 0
uni_modules/mqtt/examples/client/simple-both.js

@@ -0,0 +1,13 @@
+'use strict'
+
+var mqtt = require('../..')
+var client = mqtt.connect()
+
+// or var client = mqtt.connect({ port: 1883, host: '192.168.1.100', keepalive: 10000});
+
+client.subscribe('presence')
+client.publish('presence', 'bin hier')
+client.on('message', function (topic, message) {
+  console.log(message)
+})
+client.end()

+ 7 - 0
uni_modules/mqtt/examples/client/simple-publish.js

@@ -0,0 +1,7 @@
+'use strict'
+
+var mqtt = require('../..')
+var client = mqtt.connect()
+
+client.publish('presence', 'hello!')
+client.end()

+ 9 - 0
uni_modules/mqtt/examples/client/simple-subscribe.js

@@ -0,0 +1,9 @@
+'use strict'
+
+var mqtt = require('../..')
+var client = mqtt.connect()
+
+client.subscribe('presence')
+client.on('message', function (topic, message) {
+  console.log(message)
+})

+ 34 - 0
uni_modules/mqtt/examples/tls client/crt.ca.cg.pem

@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+MIIF7zCCA9egAwIBAgIJAOeJR1p1PU3qMA0GCSqGSIb3DQEBBQUAMIGNMQswCQYD
+VQQGEwJFUzERMA8GA1UECAwIWmFyYWdvemExETAPBgNVBAcMCFphcmFnb3phMRkw
+FwYDVQQKDBBNUVRUIGZvciBub2RlLmpzMRAwDgYDVQQLDAdNUVRULmpzMQ0wCwYD
+VQQDDARtcXR0MRwwGgYJKoZIhvcNAQkBFg1mYWtlQG1haWwuY29tMB4XDTEzMDgz
+MDEzMDIwNVoXDTIzMDgyODEzMDIwNVowgY0xCzAJBgNVBAYTAkVTMREwDwYDVQQI
+DAhaYXJhZ296YTERMA8GA1UEBwwIWmFyYWdvemExGTAXBgNVBAoMEE1RVFQgZm9y
+IG5vZGUuanMxEDAOBgNVBAsMB01RVFQuanMxDTALBgNVBAMMBG1xdHQxHDAaBgkq
+hkiG9w0BCQEWDWZha2VAbWFpbC5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
+ggIKAoICAQC7Of6OppOE+xwPdPcsT0w3keCa5k4ufZCqUAHex7+mLlrpjfCjQ2z6
+Rm0XBiCu9vy+xvLtbGDh5e/gocjAkkEywjbtrRMiFq5i41BNT3dzEWb9cCXvMWYa
+RxQgIqouJUz5r+TbaP1bm4gAWTHmp09ccoIs9Tykxhyc1nZxXVrEsHF4aBmuw5NJ
+ZwxK1tJTgP4m5H38Ms7ahGpByPsnMg6GBRs/Yen0mGhOsG+MU4TFiQb4bwIxg8Eu
+ntGP1gARvtmyTkkTDhfksRs+muEV17uPtdhGNS/0CGRWaZ2mjEYyD70Ijl2grLd4
+6Vz27uPaqUvbgntPNadKqFN+jEHTtptou3k6V9C8CeLHIq+5N6abfPVHBzaqyNqg
+QelzpSgQQBJ1H0CYREjzAs9uLfeep5ejW99Ik4YwtL6UrTVUyGzGgAl9mevZN5a4
+7mEY7MNUFdwigq0ZpbZmzYiuOURGYnoiy5o64balG5XH6Zh6B1WWhK7CArPVosz8
+eoQacj1WEM5d2Ivg1OLlEdD8FZDABv5CMTmRvnoFQuuIDzWVfrhdcZQ2tQuNLWrz
+YDKheCunPkAIFOlGi70Xv3DVrTCr6kixwL2p9MHTzF4xiWWtiOv41ZXHTMG0t2I3
+YmA45FEO5JawebPgUoGhoc2vgIw5Jo9dcGtwLCqBHSnCojPoTipVhQIDAQABo1Aw
+TjAdBgNVHQ4EFgQU1yVv/ezoDLs+qjbx0O4KiHpC41swHwYDVR0jBBgwFoAU1yVv
+/ezoDLs+qjbx0O4KiHpC41swDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOC
+AgEAScnfOewEk59DgfICJJ2vhcI33wqqn54zhJ1pi8SX3e7PLv26UEUXZaddIqeZ
+JzA/IWF+GCBQFAL7Z+sI4djXx/UpZp5ptCQBFc0tinHk1CGlC0E+LI3JS/cnFf+2
+L8VKZHbSf4ua2f/VMJo7uoyrw/gQHgUToAlYYWpGcIKKm7d0JYQE60wlHk9TXgCc
+s9XAwI+bP9VKNQkZCeooODG/5VcxdJafZSU3rW1WniFcD/R+ZNq7FZYbM+2u2mRt
+Qm7Hh/FjrN4Hnmf3xdNUE0NLHznwk4CD6EeQukN12yP2ccubnG6Z7HFFdV0g9fEP
+AVMsgY/9E9Te/BBoQKjhIg8c274ozIOsCHODx15Mn52848sq0LIQjyeOH4rtuWLL
+1dFE1ysY2gzSMUtrP+on+r6F1GkndFszxfDrBcZMXs85VAy3eKfY/jzUMrdfn0YJ
+36Wz7F40vnOUd2ni24kaOfnRodbu3lOEYD6l5fDGP79kfITyy+dtL6ExTLZQmEn+
+xKsWM9bBkV4STpFiTF61tJwzlcAL1ZDLqDaSwsM8UDZopnDgvklNoJK9XzdLwD1X
+PofOtUe08G4tq5cBDVURLKif+7EfCyAqvUptQ3MJarhoXzhDy9CjtN8TmWexKC1q
+kB5DBML0Y4NnqTEnfYCs/XFPosaS+0GximGySJcg08ay6ZA=
+-----END CERTIFICATE-----

+ 48 - 0
uni_modules/mqtt/examples/tls client/mqttclient.js

@@ -0,0 +1,48 @@
+'use strict'
+
+/** ************************** IMPORTANT NOTE ***********************************
+
+  The certificate used on this example has been generated for a host named stark.
+  So as host we SHOULD use stark if we want the server to be authorized.
+  For testing this we should add on the computer running this example a line on
+  the hosts file:
+  /etc/hosts [UNIX]
+  OR
+  \System32\drivers\etc\hosts [Windows]
+
+  The line to add on the file should be as follows:
+  <the ip address of the server> stark
+ *******************************************************************************/
+
+var mqtt = require('mqtt')
+var fs = require('fs')
+var path = require('path')
+var KEY = fs.readFileSync(path.join(__dirname, '/tls-key.pem'))
+var CERT = fs.readFileSync(path.join(__dirname, '/tls-cert.pem'))
+var TRUSTED_CA_LIST = fs.readFileSync(path.join(__dirname, '/crt.ca.cg.pem'))
+
+var PORT = 1883
+var HOST = 'stark'
+
+var options = {
+  port: PORT,
+  host: HOST,
+  key: KEY,
+  cert: CERT,
+  rejectUnauthorized: true,
+  // The CA list will be used to determine if server is authorized
+  ca: TRUSTED_CA_LIST,
+  protocol: 'mqtts'
+}
+
+var client = mqtt.connect(options)
+
+client.subscribe('messages')
+client.publish('messages', 'Current time is: ' + new Date())
+client.on('message', function (topic, message) {
+  console.log(message)
+})
+
+client.on('connect', function () {
+  console.log('Connected')
+})

+ 13 - 0
uni_modules/mqtt/examples/tls client/tls-cert.pem

@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIICATCCAWoCCQC2pNY4sfld/jANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB
+VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMB4XDTEzMDgyNzEyNTU0NVoXDTEzMDkyNjEyNTU0NVowRTELMAkG
+A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0
+IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAzXGU
+1mZUBqLwoP1fWkiZeypiKgWICUdNm+d2JHXnpQMEVBxSvsaRGOnzWVvgbMVxmD7n
+5/p9qQGTj8FY/+t2NHpbt1I9lGV0+BlZxGJvyvHikEAXPD85EEFhqSbDwgkVuMqa
+w08njqhJJ37fbd2ux6w4woRrDTN4r9CNMhFb9QECAwEAATANBgkqhkiG9w0BAQUF
+AAOBgQBIlZYo1rf8GlISuV1haSBm8U/uiyjIX/pTE5Cs7Kb84SPzKB0tHnGGCa2t
+Lu+TEwetF3NatuI1biqYuevQSfmEM75zsRSwt1P40sJ2y9B1XRTdamHOHCYCJG/b
+rti7WJYjvO8JsCUeB6M+5jFodbmvjsGgAHLLUINXrxOqYe+PWg==
+-----END CERTIFICATE-----

+ 15 - 0
uni_modules/mqtt/examples/tls client/tls-key.pem

@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDNcZTWZlQGovCg/V9aSJl7KmIqBYgJR02b53YkdeelAwRUHFK+
+xpEY6fNZW+BsxXGYPufn+n2pAZOPwVj/63Y0elu3Uj2UZXT4GVnEYm/K8eKQQBc8
+PzkQQWGpJsPCCRW4yprDTyeOqEknft9t3a7HrDjChGsNM3iv0I0yEVv1AQIDAQAB
+AoGBALv9P+WEE0VTWf7mepdBsXfbi6HKF/Xtkh2kCh5I6WO8Q/y3Qhwh1OnIQg41
+nUHK1iwq+8fxFYVN1PoJQWhEzI6JdBCrn88oADo/aVm1mGN5CWr3pwn92SAVMhbw
+442AWWG81RStrr2uPhLBNE6U/4P835qM8uG4rCP+5Z5SzX7VAkEA+TptuSc0TEkL
+5B/Nml2fYNfbQvRGVzyCbdCXdgkeZt5xuSuwDgC4GvWgjL+SAN1fjTek/Iez5NnL
+xHa5w93j2wJBANMGmRTaTxvpGdkUi/utTPtCp6GXL7hS9v41LClmQTYBOYscPn2b
+Dny2fyZPp29sZ7+AvXHWZxw7QtH+jO2Xz1MCQCI7vlqSYgKgffulyq4LchrxS3LU
+7tyIuTmwTz2tXvmuUFyo/ZPO0XsShi0PG1T3E2roW8c8NJ+Ysv6XeEjJL8UCQG0Z
+/S0tzTa15no4SEM/jwxcosRFoRNgOXimTwW8azybl3+Xg6t27h+GTuikyAEwf9cf
+nVJssfSDowFk5MG1+icCQQCqBOTXEukcJRXZixkpfEuuvS3RNzOYwG4ReKjpvWPy
+EvsfHoCsO1Sz9qz8DXpwl3GEWUGGTfWwBfereX6HLXj+
+-----END RSA PRIVATE KEY-----

+ 49 - 0
uni_modules/mqtt/examples/wss/client.js

@@ -0,0 +1,49 @@
+'use strict'
+
+var mqtt = require('mqtt')
+
+var clientId = 'mqttjs_' + Math.random().toString(16).substr(2, 8)
+
+var host = 'wss://localhost:3001/Mosca'
+
+var options = {
+  keepalive: 10,
+  clientId: clientId,
+  protocolId: 'MQTT',
+  protocolVersion: 4,
+  clean: true,
+  reconnectPeriod: 1000,
+  connectTimeout: 30 * 1000,
+  will: {
+    topic: 'WillMsg',
+    payload: 'Connection Closed abnormally..!',
+    qos: 0,
+    retain: false
+  },
+  username: 'demo',
+  password: 'demo',
+  rejectUnauthorized: false
+}
+
+var client = mqtt.connect(host, options)
+
+client.on('error', function (err) {
+  console.log(err)
+  client.end()
+})
+
+client.on('connect', function () {
+  console.log('client connected:' + clientId)
+})
+
+client.subscribe('topic', { qos: 0 })
+
+client.publish('topic', 'wss secure connection demo...!', { qos: 0, retain: false })
+
+client.on('message', function (topic, message, packet) {
+  console.log('Received Message:= ' + message.toString() + '\nOn topic:= ' + topic)
+})
+
+client.on('close', function () {
+  console.log(clientId + ' disconnected')
+})

+ 58 - 0
uni_modules/mqtt/examples/wss/client_with_proxy.js

@@ -0,0 +1,58 @@
+'use strict'
+
+var mqtt = require('mqtt')
+var HttpsProxyAgent = require('https-proxy-agent')
+var url = require('url')
+/*
+host: host of the endpoint you want to connect e.g. my.mqqt.host.com
+path: path to you endpoint e.g. '/foo/bar/mqtt'
+*/
+var endpoint = 'wss://<host><path>'
+/* create proxy agent
+proxy: your proxy e.g. proxy.foo.bar.com
+port: http proxy port e.g. 8080
+*/
+var proxy = process.env.http_proxy || 'http://<proxy>:<port>'
+var parsed = url.parse(endpoint)
+var proxyOpts = url.parse(proxy)
+// true for wss
+proxyOpts.secureEndpoint = parsed.protocol ? parsed.protocol === 'wss:' : true
+var agent = new HttpsProxyAgent(proxyOpts)
+var wsOptions = {
+  agent: agent
+  // other wsOptions
+  // foo:'bar'
+}
+var mqttOptions = {
+  keepalive: 60,
+  reschedulePings: true,
+  protocolId: 'MQTT',
+  protocolVersion: 4,
+  reconnectPeriod: 1000,
+  connectTimeout: 30 * 1000,
+  clean: true,
+  clientId: 'testClient',
+  wsOptions: wsOptions
+}
+
+var client = mqtt.connect(parsed, mqttOptions)
+
+client.on('connect', function () {
+  console.log('connected')
+})
+
+client.on('error', function (a) {
+  console.log('error!' + a)
+})
+
+client.on('offline', function (a) {
+  console.log('lost connection!' + a)
+})
+
+client.on('close', function (a) {
+  console.log('connection closed!' + a)
+})
+
+client.on('message', function (topic, message) {
+  console.log(message.toString())
+})

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1460 - 0
uni_modules/mqtt/lib/client.js


+ 130 - 0
uni_modules/mqtt/lib/connect/ali.js

@@ -0,0 +1,130 @@
+'use strict'
+
+var Transform = require('readable-stream').Transform
+var duplexify = require('duplexify')
+var base64 = require('base64-js')
+
+/* global FileReader */
+var my
+var proxy
+var stream
+var isInitialized = false
+
+function buildProxy () {
+  var proxy = new Transform()
+  proxy._write = function (chunk, encoding, next) {
+    my.sendSocketMessage({
+      data: chunk.buffer,
+      success: function () {
+        next()
+      },
+      fail: function () {
+        next(new Error())
+      }
+    })
+  }
+  proxy._flush = function socketEnd (done) {
+    my.closeSocket({
+      success: function () {
+        done()
+      }
+    })
+  }
+
+  return proxy
+}
+
+function setDefaultOpts (opts) {
+  if (!opts.hostname) {
+    opts.hostname = 'localhost'
+  }
+  if (!opts.path) {
+    opts.path = '/'
+  }
+
+  if (!opts.wsOptions) {
+    opts.wsOptions = {}
+  }
+}
+
+function buildUrl (opts, client) {
+  var protocol = opts.protocol === 'alis' ? 'wss' : 'ws'
+  var url = protocol + '://' + opts.hostname + opts.path
+  if (opts.port && opts.port !== 80 && opts.port !== 443) {
+    url = protocol + '://' + opts.hostname + ':' + opts.port + opts.path
+  }
+  if (typeof (opts.transformWsUrl) === 'function') {
+    url = opts.transformWsUrl(url, opts, client)
+  }
+  return url
+}
+
+function bindEventHandler () {
+  if (isInitialized) return
+
+  isInitialized = true
+
+  my.onSocketOpen(function () {
+    stream.setReadable(proxy)
+    stream.setWritable(proxy)
+    stream.emit('connect')
+  })
+
+  my.onSocketMessage(function (res) {
+    if (typeof res.data === 'string') {
+      var array = base64.toByteArray(res.data)
+      var buffer = Buffer.from(array)
+      proxy.push(buffer)
+    } else {
+      var reader = new FileReader()
+      reader.addEventListener('load', function () {
+        var data = reader.result
+
+        if (data instanceof ArrayBuffer) data = Buffer.from(data)
+        else data = Buffer.from(data, 'utf8')
+        proxy.push(data)
+      })
+      reader.readAsArrayBuffer(res.data)
+    }
+  })
+
+  my.onSocketClose(function () {
+    stream.end()
+    stream.destroy()
+  })
+
+  my.onSocketError(function (res) {
+    stream.destroy(res)
+  })
+}
+
+function buildStream (client, opts) {
+  opts.hostname = opts.hostname || opts.host
+
+  if (!opts.hostname) {
+    throw new Error('Could not determine host. Specify host manually.')
+  }
+
+  var websocketSubProtocol =
+    (opts.protocolId === 'MQIsdp') && (opts.protocolVersion === 3)
+      ? 'mqttv3.1'
+      : 'mqtt'
+
+  setDefaultOpts(opts)
+
+  var url = buildUrl(opts, client)
+  my = opts.my
+  my.connectSocket({
+    url: url,
+    protocols: websocketSubProtocol
+  })
+
+  proxy = buildProxy()
+  stream = duplexify.obj()
+
+  bindEventHandler()
+
+  return stream
+}
+
+module.exports = buildStream

+ 157 - 0
uni_modules/mqtt/lib/connect/index.js

@@ -0,0 +1,157 @@
+'use strict'
+
+var MqttClient = require('../client')
+var Store = require('../store')
+var url = require('url')
+var xtend = require('xtend')
+var protocols = {}
+
+if (process.title !== 'browser') {
+  protocols.mqtt = require('./tcp')
+  protocols.tcp = require('./tcp')
+  protocols.ssl = require('./tls')
+  protocols.tls = require('./tls')
+  protocols.mqtts = require('./tls')
+} else {
+  protocols.wx = require('./wx')
+  protocols.wxs = require('./wx')
+
+  protocols.ali = require('./ali')
+  protocols.alis = require('./ali')
+}
+
+protocols.ws = require('./ws')
+protocols.wss = require('./ws')
+
+/**
+ * Parse the auth attribute and merge username and password in the options object.
+ *
+ * @param {Object} [opts] option object
+ */
+function parseAuthOptions (opts) {
+  var matches
+  if (opts.auth) {
+    matches = opts.auth.match(/^(.+):(.+)$/)
+    if (matches) {
+      opts.username = matches[1]
+      opts.password = matches[2]
+    } else {
+      opts.username = opts.auth
+    }
+  }
+}
+
+/**
+ * connect - connect to an MQTT broker.
+ *
+ * @param {String} [brokerUrl] - url of the broker, optional
+ * @param {Object} opts - see MqttClient#constructor
+ */
+function connect (brokerUrl, opts) {
+  if ((typeof brokerUrl === 'object') && !opts) {
+    opts = brokerUrl
+    brokerUrl = null
+  }
+
+  opts = opts || {}
+
+  if (brokerUrl) {
+    var parsed = url.parse(brokerUrl, true)
+    if (parsed.port != null) {
+      parsed.port = Number(parsed.port)
+    }
+
+    opts = xtend(parsed, opts)
+
+    if (opts.protocol === null) {
+      throw new Error('Missing protocol')
+    }
+    opts.protocol = opts.protocol.replace(/:$/, '')
+  }
+
+  // merge in the auth options if supplied
+  parseAuthOptions(opts)
+
+  // support clientId passed in the query string of the url
+  if (opts.query && typeof opts.query.clientId === 'string') {
+    opts.clientId = opts.query.clientId
+  }
+
+  if (opts.cert && opts.key) {
+    if (opts.protocol) {
+      if (['mqtts', 'wss', 'wxs', 'alis'].indexOf(opts.protocol) === -1) {
+        switch (opts.protocol) {
+          case 'mqtt':
+            opts.protocol = 'mqtts'
+            break
+          case 'ws':
+            opts.protocol = 'wss'
+            break
+          case 'wx':
+            opts.protocol = 'wxs'
+            break
+          case 'ali':
+            opts.protocol = 'alis'
+            break
+          default:
+            throw new Error('Unknown protocol for secure connection: "' + opts.protocol + '"!')
+        }
+      }
+    } else {
+      // don't know what protocol he want to use, mqtts or wss
+      throw new Error('Missing secure protocol key')
+    }
+  }
+
+  if (!protocols[opts.protocol]) {
+    var isSecure = ['mqtts', 'wss'].indexOf(opts.protocol) !== -1
+    opts.protocol = [
+      'mqtt',
+      'mqtts',
+      'ws',
+      'wss',
+      'wx',
+      'wxs',
+      'ali',
+      'alis'
+    ].filter(function (key, index) {
+      if (isSecure && index % 2 === 0) {
+        // Skip insecure protocols when requesting a secure one.
+        return false
+      }
+      return (typeof protocols[key] === 'function')
+    })[0]
+  }
+
+  if (opts.clean === false && !opts.clientId) {
+    throw new Error('Missing clientId for unclean clients')
+  }
+
+  if (opts.protocol) {
+    opts.defaultProtocol = opts.protocol
+  }
+
+  function wrapper (client) {
+    if (opts.servers) {
+      if (!client._reconnectCount || client._reconnectCount === opts.servers.length) {
+        client._reconnectCount = 0
+      }
+
+      opts.host = opts.servers[client._reconnectCount].host
+      opts.port = opts.servers[client._reconnectCount].port
+      opts.protocol = (!opts.servers[client._reconnectCount].protocol ? opts.defaultProtocol : opts.servers[client._reconnectCount].protocol)
+      opts.hostname = opts.host
+
+      client._reconnectCount++
+    }
+
+    return protocols[opts.protocol](client, opts)
+  }
+
+  return new MqttClient(wrapper, opts)
+}
+
+module.exports = connect
+module.exports.connect = connect
+module.exports.MqttClient = MqttClient
+module.exports.Store = Store

+ 19 - 0
uni_modules/mqtt/lib/connect/tcp.js

@@ -0,0 +1,19 @@
+'use strict'
+var net = require('net')
+
+/*
+  variables port and host can be removed since
+  you have all required information in opts object
+*/
+function buildBuilder (client, opts) {
+  var port, host
+  opts.port = opts.port || 1883
+  opts.hostname = opts.hostname || opts.host || 'localhost'
+
+  port = opts.port
+  host = opts.hostname
+
+  return net.createConnection(port, host)
+}
+
+module.exports = buildBuilder

+ 41 - 0
uni_modules/mqtt/lib/connect/tls.js

@@ -0,0 +1,41 @@
+'use strict'
+var tls = require('tls')
+
+function buildBuilder (mqttClient, opts) {
+  var connection
+  opts.port = opts.port || 8883
+  opts.host = opts.hostname || opts.host || 'localhost'
+
+  opts.rejectUnauthorized = opts.rejectUnauthorized !== false
+
+  delete opts.path
+
+  connection = tls.connect(opts)
+  /* eslint no-use-before-define: [2, "nofunc"] */
+  connection.on('secureConnect', function () {
+    if (opts.rejectUnauthorized && !connection.authorized) {
+      connection.emit('error', new Error('TLS not authorized'))
+    } else {
+      connection.removeListener('error', handleTLSerrors)
+    }
+  })
+
+  function handleTLSerrors (err) {
+    // How can I get verify this error is a tls error?
+    if (opts.rejectUnauthorized) {
+      mqttClient.emit('error', err)
+    }
+
+    // close this connection to match the behaviour of net
+    // otherwise all we get is an error from the connection
+    // and close event doesn't fire. This is a work around
+    // to enable the reconnect code to work the same as with
+    // net.createConnection
+    connection.end()
+  }
+
+  connection.on('error', handleTLSerrors)
+  return connection
+}
+
+module.exports = buildBuilder

+ 92 - 0
uni_modules/mqtt/lib/connect/ws.js

@@ -0,0 +1,92 @@
+'use strict'
+
+var websocket = require('websocket-stream')
+var urlModule = require('url')
+var WSS_OPTIONS = [
+  'rejectUnauthorized',
+  'ca',
+  'cert',
+  'key',
+  'pfx',
+  'passphrase'
+]
+var IS_BROWSER = process.title === 'browser'
+
+function buildUrl (opts, client) {
+  var url = opts.protocol + '://' + opts.hostname + ':' + opts.port + opts.path
+  if (typeof (opts.transformWsUrl) === 'function') {
+    url = opts.transformWsUrl(url, opts, client)
+  }
+  return url
+}
+
+function setDefaultOpts (opts) {
+  if (!opts.hostname) {
+    opts.hostname = 'localhost'
+  }
+  if (!opts.port) {
+    if (opts.protocol === 'wss') {
+      opts.port = 443
+    } else {
+      opts.port = 80
+    }
+  }
+  if (!opts.path) {
+    opts.path = '/'
+  }
+
+  if (!opts.wsOptions) {
+    opts.wsOptions = {}
+  }
+  if (!IS_BROWSER && opts.protocol === 'wss') {
+    // Add cert/key/ca etc options
+    WSS_OPTIONS.forEach(function (prop) {
+      if (opts.hasOwnProperty(prop) && !opts.wsOptions.hasOwnProperty(prop)) {
+        opts.wsOptions[prop] = opts[prop]
+      }
+    })
+  }
+}
+
+function createWebSocket (client, opts) {
+  var websocketSubProtocol =
+    (opts.protocolId === 'MQIsdp') && (opts.protocolVersion === 3)
+      ? 'mqttv3.1'
+      : 'mqtt'
+
+  setDefaultOpts(opts)
+  var url = buildUrl(opts, client)
+  return websocket(url, [websocketSubProtocol], opts.wsOptions)
+}
+
+function buildBuilder (client, opts) {
+  return createWebSocket(client, opts)
+}
+
+function buildBuilderBrowser (client, opts) {
+  if (!opts.hostname) {
+    opts.hostname = opts.host
+  }
+
+  if (!opts.hostname) {
+    // Throwing an error in a Web Worker if no `hostname` is given, because we
+    // can not determine the `hostname` automatically.  If connecting to
+    // localhost, please supply the `hostname` as an argument.
+    if (typeof (document) === 'undefined') {
+      throw new Error('Could not determine host. Specify host manually.')
+    }
+    var parsed = urlModule.parse(document.URL)
+    opts.hostname = parsed.hostname
+
+    if (!opts.port) {
+      opts.port = parsed.port
+    }
+  }
+  return createWebSocket(client, opts)
+}
+
+if (IS_BROWSER) {
+  module.exports = buildBuilderBrowser
+} else {
+  module.exports = buildBuilder
+}

+ 134 - 0
uni_modules/mqtt/lib/connect/wx.js

@@ -0,0 +1,134 @@
+'use strict'
+
+var Transform = require('readable-stream').Transform
+var duplexify = require('duplexify')
+
+/* global wx */
+var socketTask
+var proxy
+var stream
+
+function buildProxy () {
+  var proxy = new Transform()
+  proxy._write = function (chunk, encoding, next) {
+    socketTask.send({
+      data: chunk.buffer,
+      success: function () {
+        next()
+      },
+      fail: function (errMsg) {
+        next(new Error(errMsg))
+      }
+    })
+  }
+  proxy._flush = function socketEnd (done) {
+    socketTask.close({
+      success: function () {
+        done()
+      }
+    })
+  }
+
+  return proxy
+}
+
+function setDefaultOpts (opts) {
+  if (!opts.hostname) {
+    opts.hostname = 'localhost'
+  }
+  if (!opts.path) {
+    opts.path = '/'
+  }
+
+  if (!opts.wsOptions) {
+    opts.wsOptions = {}
+  }
+}
+
+function buildUrl (opts, client) {
+  var protocol = opts.protocol === 'wxs' ? 'wss' : 'ws'
+  var url = protocol + '://' + opts.hostname + opts.path
+  if (opts.port && opts.port !== 80 && opts.port !== 443) {
+    url = protocol + '://' + opts.hostname + ':' + opts.port + opts.path
+  }
+  if (typeof (opts.transformWsUrl) === 'function') {
+    url = opts.transformWsUrl(url, opts, client)
+  }
+  return url
+}
+
+function bindEventHandler () {
+  socketTask.onOpen(function () {
+    stream.setReadable(proxy)
+    stream.setWritable(proxy)
+    stream.emit('connect')
+  })
+
+  socketTask.onMessage(function (res) {
+    var data = res.data
+
+    if (data instanceof ArrayBuffer) data = Buffer.from(data)
+    else data = Buffer.from(data, 'utf8')
+    proxy.push(data)
+  })
+
+  socketTask.onClose(function () {
+    stream.end()
+    stream.destroy()
+  })
+
+  socketTask.onError(function (res) {
+    stream.destroy(new Error(res.errMsg))
+  })
+}
+
+function buildStream (client, opts) {
+  opts.hostname = opts.hostname || opts.host
+
+  if (!opts.hostname) {
+    throw new Error('Could not determine host. Specify host manually.')
+  }
+
+  var websocketSubProtocol =
+    (opts.protocolId === 'MQIsdp') && (opts.protocolVersion === 3)
+      ? 'mqttv3.1'
+      : 'mqtt'
+
+  setDefaultOpts(opts)
+
+  var url = buildUrl(opts, client)
+  socketTask = wx.connectSocket({
+    url: url,
+    protocols: websocketSubProtocol
+  })
+
+  proxy = buildProxy()
+  stream = duplexify.obj()
+  stream._destroy = function (err, cb) {
+    socketTask.close({
+      success: function () {
+        cb && cb(err)
+      }
+    })
+  }
+
+  var destroyRef = stream.destroy
+  stream.destroy = function () {
+    stream.destroy = destroyRef
+
+    var self = this
+    process.nextTick(function () {
+      socketTask.close({
+        fail: function () {
+          self._destroy(new Error())
+        }
+      })
+    })
+  }.bind(stream)
+
+  bindEventHandler()
+
+  return stream
+}
+
+module.exports = buildStream

+ 140 - 0
uni_modules/mqtt/lib/store.js

@@ -0,0 +1,140 @@
+'use strict'
+
+/**
+ * Module dependencies
+ */
+var xtend = require('xtend')
+
+var Readable = require('readable-stream').Readable
+var streamsOpts = { objectMode: true }
+var defaultStoreOptions = {
+  clean: true
+}
+
+/**
+ * es6-map can preserve insertion order even if ES version is older.
+ *
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#Description
+ * It should be noted that a Map which is a map of an object, especially
+ * a dictionary of dictionaries, will only map to the object's insertion
+ * order. In ES2015 this is ordered for objects but for older versions of
+ * ES, this may be random and not ordered.
+ *
+ */
+var Map = require('es6-map')
+
+/**
+ * In-memory implementation of the message store
+ * This can actually be saved into files.
+ *
+ * @param {Object} [options] - store options
+ */
+function Store (options) {
+  if (!(this instanceof Store)) {
+    return new Store(options)
+  }
+
+  this.options = options || {}
+
+  // Defaults
+  this.options = xtend(defaultStoreOptions, options)
+
+  this._inflights = new Map()
+}
+
+/**
+ * Adds a packet to the store, a packet is
+ * anything that has a messageId property.
+ *
+ */
+Store.prototype.put = function (packet, cb) {
+  this._inflights.set(packet.messageId, packet)
+
+  if (cb) {
+    cb()
+  }
+
+  return this
+}
+
+/**
+ * Creates a stream with all the packets in the store
+ *
+ */
+Store.prototype.createStream = function () {
+  var stream = new Readable(streamsOpts)
+  var destroyed = false
+  var values = []
+  var i = 0
+
+  this._inflights.forEach(function (value, key) {
+    values.push(value)
+  })
+
+  stream._read = function () {
+    if (!destroyed && i < values.length) {
+      this.push(values[i++])
+    } else {
+      this.push(null)
+    }
+  }
+
+  stream.destroy = function () {
+    if (destroyed) {
+      return
+    }
+
+    var self = this
+
+    destroyed = true
+
+    process.nextTick(function () {
+      self.emit('close')
+    })
+  }
+
+  return stream
+}
+
+/**
+ * deletes a packet from the store.
+ */
+Store.prototype.del = function (packet, cb) {
+  packet = this._inflights.get(packet.messageId)
+  if (packet) {
+    this._inflights.delete(packet.messageId)
+    cb(null, packet)
+  } else if (cb) {
+    cb(new Error('missing packet'))
+  }
+
+  return this
+}
+
+/**
+ * get a packet from the store.
+ */
+Store.prototype.get = function (packet, cb) {
+  packet = this._inflights.get(packet.messageId)
+  if (packet) {
+    cb(null, packet)
+  } else if (cb) {
+    cb(new Error('missing packet'))
+  }
+
+  return this
+}
+
+/**
+ * Close the store
+ */
+Store.prototype.close = function (cb) {
+  if (this.options.clean) {
+    this._inflights = null
+  }
+  if (cb) {
+    cb()
+  }
+}
+
+module.exports = Store

+ 52 - 0
uni_modules/mqtt/lib/validations.js

@@ -0,0 +1,52 @@
+'use strict'
+
+/**
+ * Validate a topic to see if it's valid or not.
+ * A topic is valid if it follow below rules:
+ * - Rule #1: If any part of the topic is not `+` or `#`, then it must not contain `+` and '#'
+ * - Rule #2: Part `#` must be located at the end of the mailbox
+ *
+ * @param {String} topic - A topic
+ * @returns {Boolean} If the topic is valid, returns true. Otherwise, returns false.
+ */
+function validateTopic (topic) {
+  var parts = topic.split('/')
+
+  for (var i = 0; i < parts.length; i++) {
+    if (parts[i] === '+') {
+      continue
+    }
+
+    if (parts[i] === '#') {
+      // for Rule #2
+      return i === parts.length - 1
+    }
+
+    if (parts[i].indexOf('+') !== -1 || parts[i].indexOf('#') !== -1) {
+      return false
+    }
+  }
+
+  return true
+}
+
+/**
+ * Validate an array of topics to see if any of them is valid or not
+  * @param {Array} topics - Array of topics
+ * @returns {String} If the topics is valid, returns null. Otherwise, returns the invalid one
+ */
+function validateTopics (topics) {
+  if (topics.length === 0) {
+    return 'empty_topic_list'
+  }
+  for (var i = 0; i < topics.length; i++) {
+    if (!validateTopic(topics[i])) {
+      return topics[i]
+    }
+  }
+  return null
+}
+
+module.exports = {
+  validateTopics: validateTopics
+}

+ 41 - 0
uni_modules/mqtt/mqtt.js

@@ -0,0 +1,41 @@
+#!/usr/bin/env node
+'use strict'
+
+/*
+ * Copyright (c) 2015-2015 MQTT.js contributors.
+ * Copyright (c) 2011-2014 Adam Rudd.
+ *
+ * See LICENSE for more information
+ */
+
+var MqttClient = require('./lib/client')
+var connect = require('./lib/connect')
+var Store = require('./lib/store')
+
+module.exports.connect = connect
+
+// Expose MqttClient
+module.exports.MqttClient = MqttClient
+module.exports.Client = MqttClient
+module.exports.Store = Store
+
+function cli () {
+  var commist = require('commist')()
+  var helpMe = require('help-me')()
+
+  commist.register('publish', require('./bin/pub'))
+  commist.register('subscribe', require('./bin/sub'))
+  commist.register('version', function () {
+    console.log('MQTT.js version:', require('./package.json').version)
+  })
+  commist.register('help', helpMe.toStdout)
+
+  if (commist.parse(process.argv.slice(2)) !== null) {
+    console.log('No such command:', process.argv[2], '\n')
+    helpMe.toStdout()
+  }
+}
+
+if (require.main === module) {
+  cli()
+}

+ 112 - 0
uni_modules/mqtt/package.json

@@ -0,0 +1,112 @@
+{
+  "name": "mqtt",
+  "description": "A library for the MQTT protocol",
+  "version": "3.0.0",
+  "contributors": [
+    "Adam Rudd <adamvrr@gmail.com>",
+    "Matteo Collina <matteo.collina@gmail.com> (https://github.com/mcollina)",
+    "Siarhei Buntsevich <scarry0506@gmail.com> (https://github.com/scarry1992)"
+  ],
+  "keywords": [
+    "mqtt",
+    "publish/subscribe",
+    "publish",
+    "subscribe"
+  ],
+  "license": "MIT",
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/mqttjs/MQTT.js.git"
+  },
+  "main": "mqtt.js",
+  "types": "types/index.d.ts",
+  "scripts": {
+    "test": "node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly --",
+    "pretest": "standard | snazzy",
+    "tslint": "if [[ \"`node -v`\" != \"v4.3.2\" ]]; then tslint types/**/*.d.ts; fi",
+    "typescript-compile-test": "tsc -p test/typescript/tsconfig.json",
+    "typescript-compile-execute": "node test/typescript/*.js",
+    "typescript-test": "npm run typescript-compile-test && npm run typescript-compile-execute",
+    "prepare": "npm run browser-build",
+    "browser-build": "rimraf dist/ && mkdirp dist/ && browserify mqtt.js -s mqtt > dist/mqtt.js && uglifyjs < dist/mqtt.js > dist/mqtt.min.js",
+    "browser-test": "zuul --server test/browser/server.js --local --open test/browser/test.js",
+    "sauce-test": "zuul --server test/browser/server.js --tunnel ngrok -- test/browser/test.js",
+    "ci": "npm run tslint && npm run typescript-compile-test && npm run test && codecov"
+  },
+  "pre-commit": [
+    "test",
+    "tslint"
+  ],
+  "bin": {
+    "mqtt_pub": "./bin/pub.js",
+    "mqtt_sub": "./bin/sub.js",
+    "mqtt": "./mqtt.js"
+  },
+  "files": [
+    "dist/",
+    "CONTRIBUTING.md",
+    "doc",
+    "lib",
+    "bin",
+    "examples",
+    "test",
+    "types",
+    "mqtt.js"
+  ],
+  "engines": {
+    "node": ">=4.0.0"
+  },
+  "browser": {
+    "./mqtt.js": "./lib/connect/index.js",
+    "fs": false,
+    "tls": false,
+    "net": false
+  },
+  "dependencies": {
+    "base64-js": "^1.3.0",
+    "commist": "^1.0.0",
+    "concat-stream": "^1.6.2",
+    "end-of-stream": "^1.4.1",
+    "es6-map": "^0.1.5",
+    "help-me": "^1.0.1",
+    "inherits": "^2.0.3",
+    "minimist": "^1.2.0",
+    "mqtt-packet": "^6.0.0",
+    "pump": "^3.0.0",
+    "readable-stream": "^2.3.6",
+    "reinterval": "^1.1.0",
+    "split2": "^3.1.0",
+    "websocket-stream": "^5.1.2",
+    "xtend": "^4.0.1"
+  },
+  "devDependencies": {
+    "@types/node": "^10.0.0",
+    "browserify": "^16.2.2",
+    "codecov": "^3.0.4",
+    "global": "^4.3.2",
+    "istanbul": "^0.4.5",
+    "mkdirp": "^0.5.1",
+    "mocha": "^4.1.0",
+    "mqtt-connection": "^4.0.0",
+    "pre-commit": "^1.2.2",
+    "rimraf": "^2.6.2",
+    "safe-buffer": "^5.1.2",
+    "should": "^13.2.1",
+    "sinon": "~1.17.7",
+    "snazzy": "^8.0.0",
+    "standard": "^11.0.1",
+    "through2": "^3.0.0",
+    "tslint": "^5.11.0",
+    "tslint-config-standard": "^8.0.1",
+    "typescript": "^3.2.2",
+    "uglify-js": "^3.4.5",
+    "ws": "^3.3.3",
+    "zuul": "^3.12.0",
+    "zuul-ngrok": "^4.0.0"
+  },
+  "standard": {
+    "env": [
+      "mocha"
+    ]
+  }
+}

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 3084 - 0
uni_modules/mqtt/test/abstract_client.js


+ 135 - 0
uni_modules/mqtt/test/abstract_store.js

@@ -0,0 +1,135 @@
+'use strict'
+
+require('should')
+
+module.exports = function abstractStoreTest (build) {
+  var store
+
+  beforeEach(function (done) {
+    build(function (err, _store) {
+      store = _store
+      done(err)
+    })
+  })
+
+  afterEach(function (done) {
+    store.close(done)
+  })
+
+  it('should put and stream in-flight packets', function (done) {
+    var packet = {
+      topic: 'hello',
+      payload: 'world',
+      qos: 1,
+      messageId: 42
+    }
+
+    store.put(packet, function () {
+      store
+        .createStream()
+        .on('data', function (data) {
+          data.should.eql(packet)
+          done()
+        })
+    })
+  })
+
+  it('should support destroying the stream', function (done) {
+    var packet = {
+      topic: 'hello',
+      payload: 'world',
+      qos: 1,
+      messageId: 42
+    }
+
+    store.put(packet, function () {
+      var stream = store.createStream()
+      stream.on('close', done)
+      stream.destroy()
+    })
+  })
+
+  it('should add and del in-flight packets', function (done) {
+    var packet = {
+      topic: 'hello',
+      payload: 'world',
+      qos: 1,
+      messageId: 42
+    }
+
+    store.put(packet, function () {
+      store.del(packet, function () {
+        store
+          .createStream()
+          .on('data', function () {
+            done(new Error('this should never happen'))
+          })
+          .on('end', done)
+      })
+    })
+  })
+
+  it('should replace a packet when doing put with the same messageId', function (done) {
+    var packet1 = {
+      cmd: 'publish', // added
+      topic: 'hello',
+      payload: 'world',
+      qos: 2,
+      messageId: 42
+    }
+    var packet2 = {
+      cmd: 'pubrel', // added
+      qos: 2,
+      messageId: 42
+    }
+
+    store.put(packet1, function () {
+      store.put(packet2, function () {
+        store
+          .createStream()
+          .on('data', function (data) {
+            data.should.eql(packet2)
+            done()
+          })
+      })
+    })
+  })
+
+  it('should return the original packet on del', function (done) {
+    var packet = {
+      topic: 'hello',
+      payload: 'world',
+      qos: 1,
+      messageId: 42
+    }
+
+    store.put(packet, function () {
+      store.del({ messageId: 42 }, function (err, deleted) {
+        if (err) {
+          throw err
+        }
+        deleted.should.eql(packet)
+        done()
+      })
+    })
+  })
+
+  it('should get a packet with the same messageId', function (done) {
+    var packet = {
+      topic: 'hello',
+      payload: 'world',
+      qos: 1,
+      messageId: 42
+    }
+
+    store.put(packet, function () {
+      store.get({ messageId: 42 }, function (err, fromDb) {
+        if (err) {
+          throw err
+        }
+        fromDb.should.eql(packet)
+        done()
+      })
+    })
+  })
+}

+ 132 - 0
uni_modules/mqtt/test/browser/server.js

@@ -0,0 +1,132 @@
+'use strict'
+
+var handleClient
+var websocket = require('websocket-stream')
+var WebSocketServer = require('ws').Server
+var Connection = require('mqtt-connection')
+var http = require('http')
+
+handleClient = function (client) {
+  var self = this
+
+  if (!self.clients) {
+    self.clients = {}
+  }
+
+  client.on('connect', function (packet) {
+    if (packet.clientId === 'invalid') {
+      client.connack({returnCode: 2})
+    } else {
+      client.connack({returnCode: 0})
+    }
+    self.clients[packet.clientId] = client
+    client.subscriptions = []
+  })
+
+  client.on('publish', function (packet) {
+    var i, k, c, s, publish
+    switch (packet.qos) {
+      case 0:
+        break
+      case 1:
+        client.puback(packet)
+        break
+      case 2:
+        client.pubrec(packet)
+        break
+    }
+
+    for (k in self.clients) {
+      c = self.clients[k]
+      publish = false
+
+      for (i = 0; i < c.subscriptions.length; i++) {
+        s = c.subscriptions[i]
+
+        if (s.test(packet.topic)) {
+          publish = true
+        }
+      }
+
+      if (publish) {
+        try {
+          c.publish({topic: packet.topic, payload: packet.payload})
+        } catch (error) {
+          delete self.clients[k]
+        }
+      }
+    }
+  })
+
+  client.on('pubrel', function (packet) {
+    client.pubcomp(packet)
+  })
+
+  client.on('pubrec', function (packet) {
+    client.pubrel(packet)
+  })
+
+  client.on('pubcomp', function () {
+    // Nothing to be done
+  })
+
+  client.on('subscribe', function (packet) {
+    var qos
+    var topic
+    var reg
+    var granted = []
+
+    for (var i = 0; i < packet.subscriptions.length; i++) {
+      qos = packet.subscriptions[i].qos
+      topic = packet.subscriptions[i].topic
+      reg = new RegExp(topic.replace('+', '[^/]+').replace('#', '.+') + '$')
+
+      granted.push(qos)
+      client.subscriptions.push(reg)
+    }
+
+    client.suback({messageId: packet.messageId, granted: granted})
+  })
+
+  client.on('unsubscribe', function (packet) {
+    client.unsuback(packet)
+  })
+
+  client.on('pingreq', function () {
+    client.pingresp()
+  })
+}
+
+function start (startPort, done) {
+  var server = http.createServer()
+  var wss = new WebSocketServer({server: server})
+
+  wss.on('connection', function (ws) {
+    var stream, connection
+
+    if (!(ws.protocol === 'mqtt' ||
+          ws.protocol === 'mqttv3.1')) {
+      return ws.close()
+    }
+
+    stream = websocket(ws)
+    connection = new Connection(stream)
+    handleClient.call(server, connection)
+  })
+  server.listen(startPort, done)
+  server.on('request', function (req, res) {
+    res.statusCode = 404
+    res.end('Not Found')
+  })
+  return server
+}
+
+if (require.main === module) {
+  start(process.env.PORT || process.env.ZUUL_PORT, function (err) {
+    if (err) {
+      console.error(err)
+      return
+    }
+    console.log('tunnelled server started on port', process.env.PORT || process.env.ZUUL_PORT)
+  })
+}

+ 92 - 0
uni_modules/mqtt/test/browser/test.js

@@ -0,0 +1,92 @@
+'use strict'
+
+var mqtt = require('../../lib/connect')
+var _URL = require('url')
+var xtend = require('xtend')
+var parsed = _URL.parse(document.URL)
+var isHttps = parsed.protocol === 'https:'
+var port = parsed.port || (isHttps ? 443 : 80)
+var host = parsed.hostname
+var protocol = isHttps ? 'wss' : 'ws'
+
+function clientTests (buildClient) {
+  var client
+
+  beforeEach(function () {
+    client = buildClient()
+    client.on('offline', function () {
+      console.log('client offline')
+    })
+    client.on('connect', function () {
+      console.log('client connect')
+    })
+    client.on('reconnect', function () {
+      console.log('client reconnect')
+    })
+  })
+
+  afterEach(function (done) {
+    client.once('close', function () {
+      done()
+    })
+    client.end()
+  })
+
+  it('should connect', function (done) {
+    client.on('connect', function () {
+      done()
+    })
+  })
+
+  it('should publish and subscribe', function (done) {
+    client.subscribe('hello', function () {
+      done()
+    }).publish('hello', 'world')
+  })
+}
+
+function suiteFactory (configName, opts) {
+  function setVersion (base) {
+    return xtend(base || {}, opts)
+  }
+
+  var suiteName = 'MqttClient(' + configName + '=' + JSON.stringify(opts) + ')'
+  describe(suiteName, function () {
+    this.timeout(10000)
+
+    describe('specifying nothing', function () {
+      clientTests(function () {
+        return mqtt.connect(setVersion())
+      })
+    })
+
+    if (parsed.hostname === 'localhost') {
+      describe('specifying a port', function () {
+        clientTests(function () {
+          return mqtt.connect(setVersion({ protocol: protocol, port: port }))
+        })
+      })
+    }
+
+    describe('specifying a port and host', function () {
+      clientTests(function () {
+        return mqtt.connect(setVersion({ protocol: protocol, port: port, host: host }))
+      })
+    })
+
+    describe('specifying a URL', function () {
+      clientTests(function () {
+        return mqtt.connect(protocol + '://' + host + ':' + port, setVersion())
+      })
+    })
+
+    describe('specifying a URL with a path', function () {
+      clientTests(function () {
+        return mqtt.connect(protocol + '://' + host + ':' + port + '/mqtt', setVersion())
+      })
+    })
+  })
+}
+
+suiteFactory('v3', {protocolId: 'MQIsdp', protocolVersion: 3})
+suiteFactory('default', {})

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1129 - 0
uni_modules/mqtt/test/client.js


+ 16 - 0
uni_modules/mqtt/test/helpers/private-csr.pem

@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICijCCAXICAQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
+ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBANtzIZmqf7h3axO9mzo2VhiF/BF3Y4E/fDTkFy27
+PgssS/ipFOMe/IxyM/hA/o/bQb0BY+sH5s1020kNH79umtabWMaDmOi8bvmHWtVC
+cYhn3mhbRFWcORdTnfQ8uRYXZGeoupjlhfrKkQCoSAFKh1OzU7aNx4CjMAjSa4py
+trMAVNJ37RryhsfMuHAeG8+0Eo3qmYyaplpurtr8A3HWV65R2VFCwZ5hKG8I9X2F
+3UrYKHr4xlxOgjD8j2OfYZxpGHI6YexJ28aR0xlsWfzS+TKKFVxy8ntgPGL0ZXL3
+vss80mAcBl9FfsJzufn4IHOYspX1OEM0M7plMmQw/yNT9B8CAwEAAaAAMA0GCSqG
+SIb3DQEBBQUAA4IBAQBsONiE5HTjfR1pDrWPIhbLqMO3AqmuB5AwpQm8kAaM2Oz1
+DI/a8bHYyODMiyWUPTtwLMQWcJpAG2ZhE18gLqFwXZR1XSOxY1yF+uZ7Ls3hwzbq
+9A6O254B5wXBnXkVbzZwFshV5HWiZwVivF5GDyLRsMoS2EtUHoDEP4YIRK0kPL9H
+m3BB334KlWTc8NNXFFG62OL7q2fa8xRHlN8SYfeUjy79eEoBdHv5wL/ZN/YBCDNJ
+2zrYUvbOmfoq1e+6AczZ6xAHHeneUQuaOF225aMwHHZTiP2TlIeFXwBvzV1BWIJv
+dOaHX/f3NamKoGvwYyIR1FrI2FpXTJLRE/eu7TFD
+-----END CERTIFICATE REQUEST-----

+ 27 - 0
uni_modules/mqtt/test/helpers/private-key.pem

@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA23Mhmap/uHdrE72bOjZWGIX8EXdjgT98NOQXLbs+CyxL+KkU
+4x78jHIz+ED+j9tBvQFj6wfmzXTbSQ0fv26a1ptYxoOY6Lxu+Yda1UJxiGfeaFtE
+VZw5F1Od9Dy5FhdkZ6i6mOWF+sqRAKhIAUqHU7NTto3HgKMwCNJrinK2swBU0nft
+GvKGx8y4cB4bz7QSjeqZjJqmWm6u2vwDcdZXrlHZUULBnmEobwj1fYXdStgoevjG
+XE6CMPyPY59hnGkYcjph7EnbxpHTGWxZ/NL5MooVXHLye2A8YvRlcve+yzzSYBwG
+X0V+wnO5+fggc5iylfU4QzQzumUyZDD/I1P0HwIDAQABAoIBAQDNgNdqS5wnZs1D
+Qz/mF5QwiugugxsPoh/yd9as4LeNRwIt7ki9F/twmlHInTTGCpFZKcAkDNY6eMAR
+fNTKNA2UAw3zeLDs4ekai4KoSvx+vKYuG6m2cgGUsp0sZuD8qxM/b2auX+JDpQZ9
+Exm6+8wWucwfHE5DTI5i9In4sMweeuiEUYnndTzElkvnP/44h1fGSU1iGUKn/ftc
+P4X+3SU68KMT3kUsEBavtmSdyeG/lSFEjm73FwVIRZ+PfbQX2hDD+mmseAXGFKi1
+HudtQkEzTvYR+QAgvtjNgt/0qxFtPdj7Y+iRkCZQSJToAw8z6vwUn1qNCADauGMI
+X6KIm8XBAoGBAPiwMLYpIqp1rksINbqpbVqjtqsoejQuPYeEF7OXHbH9il7pWrQF
+wLbogo3YXX+a66RreVMhsUeq7+pIf/sK2lT73gDpFfvZnJG1ww94QkHBEPso0bN9
+pcGgceIK7KRRAiAl5Mjw6pZZNnIBxlIFaSbBqQau74NfdaalMBF2wi+3AoGBAOHm
+3ttFtVjVlb2fHoiGNZCZDv3gnsQXZlCxS+rQ4XEmEWKHAH4T3+Kzmo8jWoX+DGGD
+6UkxWHv7e+KrYIZDi7Dd2HFV0gHN6d1SNdPix3vN114bNOrbfqxuEVT5PdFHSuel
+5d3ix+3U+tpHamwb88eyeq6Q3t5Lcl3gIRGLzo7ZAoGBAKVuLzk+K/1Qw1zOXU+K
+nWAKP92j04caq3uWd13UTMC2dHGmsdvHZ+dEzHQnVisol1CM3exbIV8XavliuR/6
+nDqkQY5Bf4pFvE2Bp/yGdyzejblF8hmAn98qKBfCRKEZ8lwIWSUCfkr9laZJX+/4
+AXbypMn5XQL7YXw1rsAvTAYJAoGAV4ZL8kkf6jtWuRFdkyfsuQmUdWkCGpe2XK1U
+7LXhoyVMtw/3cOHibMOJrsvT1vaHdYDWcjVcQy084qXj0CF7jhtmMQM/StOtOMMR
+d/b1s1Idj6ia6CQDAGvk6zdmbB9jNj1gwoeLTuqmBsyEvz5VRZoxTlFzCE3TEew0
+48d3UIECgYBMxnLByVQA3pQWWIZZyqt+HgJAphYPdpnPalblQAbuCksKTZ/QKDkW
+dzih1PQROVrYrX7VwJ3/I8gXIuvKVtN1NKOS3a0JtbJQhpH4YbRwyQskXWYP8oYa
+MjBGPymNDhZh0zoGWzst5uR3NpdNV+7yNYPvyxzVNjlPjtAUqIxjBg==
+-----END RSA PRIVATE KEY-----

+ 19 - 0
uni_modules/mqtt/test/helpers/public-cert.pem

@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDBjCCAe4CCQDkrq1PMPtmfzANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB
+VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMB4XDTEzMDEyNTEwMzEyOVoXDTEzMDIyNDEwMzEyOVowRTELMAkG
+A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0
+IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ANtzIZmqf7h3axO9mzo2VhiF/BF3Y4E/fDTkFy27PgssS/ipFOMe/IxyM/hA/o/b
+Qb0BY+sH5s1020kNH79umtabWMaDmOi8bvmHWtVCcYhn3mhbRFWcORdTnfQ8uRYX
+ZGeoupjlhfrKkQCoSAFKh1OzU7aNx4CjMAjSa4pytrMAVNJ37RryhsfMuHAeG8+0
+Eo3qmYyaplpurtr8A3HWV65R2VFCwZ5hKG8I9X2F3UrYKHr4xlxOgjD8j2OfYZxp
+GHI6YexJ28aR0xlsWfzS+TKKFVxy8ntgPGL0ZXL3vss80mAcBl9FfsJzufn4IHOY
+spX1OEM0M7plMmQw/yNT9B8CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAeHAwoKYl
+6g9lUEwBDqm6ZxjgoYQi6V3loCjBcTr5OrMkLvvZrA55xsse0NRH40I/pvCaAZAZ
+EEna0fr5GPYi+y+I8EoU2W/+ehSqRAU8Fkdm0eR5MjyLWYOwd3ClUND8EpUNNSKH
+Xw9k9EQmyKsDxVsKWoJoO9rfFkUjooz07jGPCud18QCBs5i5ThbnQ9UP+26D8z5k
+1Dii69LIcLXA3Vtm6R5fT57zNusfx8bqA9yy7UThYaXIazNMWNxiJRXfv0J4zFdD
+RQ+SFdJ3p5jurPkc3oRWWPbn/Lpf0E5XlYTJImXT1WmWnQSaNtME4P+3kEL5x+v/
+u8zTLbobG4x0rQ==
+-----END CERTIFICATE-----

+ 9 - 0
uni_modules/mqtt/test/helpers/public-key.pem

@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA23Mhmap/uHdrE72bOjZW
+GIX8EXdjgT98NOQXLbs+CyxL+KkU4x78jHIz+ED+j9tBvQFj6wfmzXTbSQ0fv26a
+1ptYxoOY6Lxu+Yda1UJxiGfeaFtEVZw5F1Od9Dy5FhdkZ6i6mOWF+sqRAKhIAUqH
+U7NTto3HgKMwCNJrinK2swBU0nftGvKGx8y4cB4bz7QSjeqZjJqmWm6u2vwDcdZX
+rlHZUULBnmEobwj1fYXdStgoevjGXE6CMPyPY59hnGkYcjph7EnbxpHTGWxZ/NL5
+MooVXHLye2A8YvRlcve+yzzSYBwGX0V+wnO5+fggc5iylfU4QzQzumUyZDD/I1P0
+HwIDAQAB
+-----END PUBLIC KEY-----

+ 52 - 0
uni_modules/mqtt/test/helpers/server.js

@@ -0,0 +1,52 @@
+'use strict'
+
+var Server = require('../server')
+var fs = require('fs')
+
+module.exports.init_server = function (PORT) {
+  var server = new Server(function (client) {
+    client.on('connect', function () {
+      client.connack(0)
+    })
+
+    client.on('publish', function (packet) {
+      switch (packet.qos) {
+        case 1:
+          client.puback({messageId: packet.messageId})
+          break
+        case 2:
+          client.pubrec({messageId: packet.messageId})
+          break
+        default:
+          break
+      }
+    })
+
+    client.on('pubrel', function (packet) {
+      client.pubcomp({messageId: packet.messageId})
+    })
+
+    client.on('pingreq', function () {
+      client.pingresp()
+    })
+
+    client.on('disconnect', function () {
+      client.stream.end()
+    })
+  })
+  server.listen(PORT)
+  return server
+}
+
+module.exports.init_secure_server = function (port, key, cert) {
+  var server = new Server.SecureServer({
+    key: fs.readFileSync(key),
+    cert: fs.readFileSync(cert)
+  }, function (client) {
+    client.on('connect', function () {
+      client.connack({returnCode: 0})
+    })
+  })
+  server.listen(port)
+  return server
+}

+ 9 - 0
uni_modules/mqtt/test/helpers/server_process.js

@@ -0,0 +1,9 @@
+'use strict'
+
+var Server = require('../server')
+
+new Server(function (client) {
+  client.on('connect', function () {
+    client.connack({ returnCode: 0 })
+  })
+}).listen(3000, 'localhost')

+ 14 - 0
uni_modules/mqtt/test/helpers/tls-cert.pem

@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICKTCCAZICCQDRSYqWgZyJmjANBgkqhkiG9w0BAQUFADBZMQswCQYDVQQGEwJB
+VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMTQwNjEzMTAwMzAzWhcN
+MjQwNjEwMTAwMzAzWjBZMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0
+ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRIwEAYDVQQDEwls
+b2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMzFv8+9EBb1sG07
+TjdtbRksRwF7/CZsOWe+ef4ZYPolC5lzvNVYXsBIjL+ilhyKopBbwnOuX9+6FmYO
+G/N1lDZRssolGoOVM+1ma3Whmxz8C1g+xi95nP2OqtwP5Du6xhvOM265CiMaf8DH
+n63ZFxyi3d1CdNGamNQvrybCzJn7AgMBAAEwDQYJKoZIhvcNAQEFBQADgYEABmyp
+3wyBGjb2zSHK5pF9c9GXyHRL4/FkP6qzU5NWrfVAowdOczctJbc3hxPh34Encbr6
+KijYnbdP7/f8aZrStLGqgFYL3SHZY3zvgLTzOmGr9reHUkubHtN+mWHeYy1wVe3D
+qEOI8ygT4olVZmWAD+VLKgAb0J07rA/PKf82fBI=
+-----END CERTIFICATE-----

+ 11 - 0
uni_modules/mqtt/test/helpers/tls-csr.pem

@@ -0,0 +1,11 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIBmTCCAQICAQAwWTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUx
+ITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAxMJbG9j
+YWxob3N0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMxb/PvRAW9bBtO043
+bW0ZLEcBe/wmbDlnvnn+GWD6JQuZc7zVWF7ASIy/opYciqKQW8Jzrl/fuhZmDhvz
+dZQ2UbLKJRqDlTPtZmt1oZsc/AtYPsYveZz9jqrcD+Q7usYbzjNuuQojGn/Ax5+t
+2Rccot3dQnTRmpjUL68mwsyZ+wIDAQABoAAwDQYJKoZIhvcNAQEFBQADgYEALjPb
+zOEL8ahD+UFxwVCXTq4MsKwMlyZCcEVY0CksAgWpCkWr54JUp832p3nEylPRj/gx
+8fKWzz5DiO3RER8fzmkb+Kwa+JvXVHmTFzemxYGnxS/HRlF0ZoeAIgvq6ouIrqm9
+1P9gsuYmA5vtfc6Y/NVlSrcSYFH4ADF5DcRTi2Q=
+-----END CERTIFICATE REQUEST-----

+ 15 - 0
uni_modules/mqtt/test/helpers/tls-key.pem

@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQDMxb/PvRAW9bBtO043bW0ZLEcBe/wmbDlnvnn+GWD6JQuZc7zV
+WF7ASIy/opYciqKQW8Jzrl/fuhZmDhvzdZQ2UbLKJRqDlTPtZmt1oZsc/AtYPsYv
+eZz9jqrcD+Q7usYbzjNuuQojGn/Ax5+t2Rccot3dQnTRmpjUL68mwsyZ+wIDAQAB
+AoGARg7p/xL6LEDGqbh+nCwOBWzGplVbAXJJeZsLdcoNCcge3dNhKcTgNf0cWnwv
+y3gLAkTClH12Q78Q5r2xBmyV1hqyEb9lrIqAlSS5GjnTWWhyzspcjKZWR5PAjOYo
+LlxNpCegWEjOUpD4Lwf9yjEu+xrDGVmsLF0PPRkAM32qh9ECQQD1vzyFr/hSn7Rh
+6IFFbLAVkIvsy+1Ca7tF6/7byHCdwqS5oUKaY+9DAr0TE+br87N2IzUCU5X7Cv74
+m+YiqhBlAkEA1VDfpq8puyIq2F6Ftx0xpYMv6XKhuRyAziT/DzIBdFVeOMIgUuk0
+7E4W0N/gDmUmEQFl3HYzUfdZrTUKzjzq3wJAZflsKOGDfu2skXBErEVUsC4iEinx
+Ez3XIUWzpQoAyUYqyqjDFYPglgL96Hu6uDCRSLWFWqjKtLi0Yv92OO4vDQJASuAk
+YQHDCCiqGWC0Vt4sewhdXPgbxDo5DCL4VIEc+ZStiga6CeBJ71hJse+jWeovPnDb
+LFNhGDhWhfHEZTgEyQJAXNuypDS5l73LPvc+yduPZiNEtwae9KbWaZUwC683a81s
+mkT7uroNYyK9ptZrz/LMJJotkqCjigXaA3kuzuNUCQ==
+-----END RSA PRIVATE KEY-----

+ 13 - 0
uni_modules/mqtt/test/helpers/wrong-cert.pem

@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIICATCCAWoCCQDEVSSDKkcTdjANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB
+VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMB4XDTE0MDUxMTE2MzMxMVoXDTE0MDYxMDE2MzMxMVowRTELMAkG
+A1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0
+IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAyDMI
+VS2XSizZT8KeFYFQfKt7CcT5/Pkzw2BDJoMVLmrkfHdddjsTgswqHfzhO8Fmfg6B
+MxgsEz2aKi24hJxQFuQ1DGhyfKHnjxM5PqSLiOkQDKllnAOgqOBDXpca0jXypCk1
+IVhMspM2ylrnBXps3nTBLJxFBkZSBov/JDkkL+cCAwEAATANBgkqhkiG9w0BAQUF
+AAOBgQA8k93U0VDIpQ8lpScxrCtEu5jLZgB1fw0fdCUtDHaaM1v+LWr1xfCmFKyT
+kUMcJl4e1pkcSNfXcI7LdNt8EJqMabOi2UpW1+VZJn206D0f3XmNSmZbk8oozGrl
+qg2wSTZYlZClCTpWO2Y+iYzojY8kmLaQ2xbTxBz1XlshC8HvsA==
+-----END CERTIFICATE-----

+ 11 - 0
uni_modules/mqtt/test/helpers/wrong-csr.pem

@@ -0,0 +1,11 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIBhDCB7gIBADBFMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEh
+MB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQDIMwhVLZdKLNlPwp4VgVB8q3sJxPn8+TPDYEMmgxUuauR8
+d112OxOCzCod/OE7wWZ+DoEzGCwTPZoqLbiEnFAW5DUMaHJ8oeePEzk+pIuI6RAM
+qWWcA6Co4ENelxrSNfKkKTUhWEyykzbKWucFemzedMEsnEUGRlIGi/8kOSQv5wID
+AQABoAAwDQYJKoZIhvcNAQEFBQADgYEAFXqd8jhW+2hRvkRB1CCVBK5e6AQHq1rF
+s3B36O64hRHIr1KC+dWr8vv1t9Rkud+7E3ELHtxWCORIYpqQ2Ddldt4PP+MTNj2C
+qgwOpxM0VDxeeWml8fqx2uzfPhVduyHGm0yff2JS2KRVmnIPLTUuz/+udukIFDVO
+Sc4/W3qY7f8=
+-----END CERTIFICATE REQUEST-----

+ 15 - 0
uni_modules/mqtt/test/helpers/wrong-key.pem

@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDIMwhVLZdKLNlPwp4VgVB8q3sJxPn8+TPDYEMmgxUuauR8d112
+OxOCzCod/OE7wWZ+DoEzGCwTPZoqLbiEnFAW5DUMaHJ8oeePEzk+pIuI6RAMqWWc
+A6Co4ENelxrSNfKkKTUhWEyykzbKWucFemzedMEsnEUGRlIGi/8kOSQv5wIDAQAB
+AoGBALOzszgaG2I2jb4dmJ7/G4s8tc2YJTlhS4iFgOEx6rJmur/KuXcmIiZXMzsF
+wftMZ76hMHH3saB3vEk+DxHh6bR6cW/I82Vxts9suz2fRnd2mh5JHI+opXE53LVn
+hJcQ4k6LJ9MNVxlHwCTrSuJvtikDOOrCARHRvYzFRL4wXvmpAkEA+DFzXGDg8PxX
+RFp6RLLbqaUT6YXNi+E5ERuumru+rgRj+OF/dxNK3d1lcIJZjqVMDAgOsZ66/bkh
+GfCzJPREUwJBAM5/HeHmTEM5K5B0X8b6XEHTgWFUNTu4K36Ee5ySd8RYI8zjQ9wS
+NM1nXnx12npL7DSkShz9xgnTe0f8YmQnc50CQQCgdE/RXCxwf6LnZNsBCOSsIzXh
+VgiRsxSSs+PI0zGuDNaY8yfV0ponH1fSSeMeLk0gxiDBwg2/tGzq+UrHzEdTAkB1
+/U5O0K+MzbLlxIkhgdaLSlYoDdyo9e/sR7j12v8SMqaqIMWajtCa+VCU3yZqMM2T
+urgaXqr03GEZ3c0+mwhFAkAwWkczV1iwuedmWLKc36iQhoj+FRMUoxWe/fBixQls
+g0lDvwWiZ3M6hjCsBRckmt8eU2mUh79Odrj5fRWIwXaX
+-----END RSA PRIVATE KEY-----

+ 4 - 0
uni_modules/mqtt/test/mocha.opts

@@ -0,0 +1,4 @@
+--check-leaks
+--timeout 5000
+--exit
+

+ 230 - 0
uni_modules/mqtt/test/mqtt.js

@@ -0,0 +1,230 @@
+'use strict'
+
+var fs = require('fs')
+var path = require('path')
+var mqtt = require('../')
+
+describe('mqtt', function () {
+  describe('#connect', function () {
+    var sslOpts, sslOpts2
+    it('should return an MqttClient when connect is called with mqtt:/ url', function () {
+      var c = mqtt.connect('mqtt://localhost:1883')
+
+      c.should.be.instanceOf(mqtt.MqttClient)
+      c.end()
+    })
+
+    it('should throw an error when called with no protocol specified', function () {
+      (function () {
+        var c = mqtt.connect('foo.bar.com')
+        c.end()
+      }).should.throw('Missing protocol')
+    })
+
+    it('should throw an error when called with no protocol specified - with options', function () {
+      (function () {
+        var c = mqtt.connect('tcp://foo.bar.com', { protocol: null })
+        c.end()
+      }).should.throw('Missing protocol')
+    })
+
+    it('should return an MqttClient with username option set', function () {
+      var c = mqtt.connect('mqtt://user:pass@localhost:1883')
+
+      c.should.be.instanceOf(mqtt.MqttClient)
+      c.options.should.have.property('username', 'user')
+      c.options.should.have.property('password', 'pass')
+      c.end()
+    })
+
+    it('should return an MqttClient with username and password options set', function () {
+      var c = mqtt.connect('mqtt://user@localhost:1883')
+
+      c.should.be.instanceOf(mqtt.MqttClient)
+      c.options.should.have.property('username', 'user')
+      c.end()
+    })
+
+    it('should return an MqttClient with the clientid with random value', function () {
+      var c = mqtt.connect('mqtt://user@localhost:1883')
+
+      c.should.be.instanceOf(mqtt.MqttClient)
+      c.options.should.have.property('clientId')
+      c.end()
+    })
+
+    it('should return an MqttClient with the clientid with empty string', function () {
+      var c = mqtt.connect('mqtt://user@localhost:1883?clientId=')
+
+      c.should.be.instanceOf(mqtt.MqttClient)
+      c.options.should.have.property('clientId', '')
+      c.end()
+    })
+
+    it('should return an MqttClient with the clientid option set', function () {
+      var c = mqtt.connect('mqtt://user@localhost:1883?clientId=123')
+
+      c.should.be.instanceOf(mqtt.MqttClient)
+      c.options.should.have.property('clientId', '123')
+      c.end()
+    })
+
+    it('should return an MqttClient when connect is called with tcp:/ url', function () {
+      var c = mqtt.connect('tcp://localhost')
+
+      c.should.be.instanceOf(mqtt.MqttClient)
+      c.end()
+    })
+
+    it('should return an MqttClient with correct host when called with a host and port', function () {
+      var c = mqtt.connect('tcp://user:pass@localhost:1883')
+
+      c.options.should.have.property('hostname', 'localhost')
+      c.options.should.have.property('port', 1883)
+      c.end()
+    })
+
+    sslOpts = {
+      keyPath: path.join(__dirname, 'helpers', 'private-key.pem'),
+      certPath: path.join(__dirname, 'helpers', 'public-cert.pem'),
+      caPaths: [path.join(__dirname, 'helpers', 'public-cert.pem')]
+    }
+
+    it('should return an MqttClient when connect is called with mqtts:/ url', function () {
+      var c = mqtt.connect('mqtts://localhost', sslOpts)
+
+      c.options.should.have.property('protocol', 'mqtts')
+
+      c.on('error', function () {})
+
+      c.should.be.instanceOf(mqtt.MqttClient)
+      c.end()
+    })
+
+    it('should return an MqttClient when connect is called with ssl:/ url', function () {
+      var c = mqtt.connect('ssl://localhost', sslOpts)
+
+      c.options.should.have.property('protocol', 'ssl')
+
+      c.on('error', function () {})
+
+      c.should.be.instanceOf(mqtt.MqttClient)
+      c.end()
+    })
+
+    it('should return an MqttClient when connect is called with ws:/ url', function () {
+      var c = mqtt.connect('ws://localhost', sslOpts)
+
+      c.options.should.have.property('protocol', 'ws')
+
+      c.on('error', function () {})
+
+      c.should.be.instanceOf(mqtt.MqttClient)
+      c.end()
+    })
+
+    it('should return an MqttClient when connect is called with wss:/ url', function () {
+      var c = mqtt.connect('wss://localhost', sslOpts)
+
+      c.options.should.have.property('protocol', 'wss')
+
+      c.on('error', function () {})
+
+      c.should.be.instanceOf(mqtt.MqttClient)
+      c.end()
+    })
+
+    sslOpts2 = {
+      key: fs.readFileSync(path.join(__dirname, 'helpers', 'private-key.pem')),
+      cert: fs.readFileSync(path.join(__dirname, 'helpers', 'public-cert.pem')),
+      ca: [fs.readFileSync(path.join(__dirname, 'helpers', 'public-cert.pem'))]
+    }
+
+    it('should throw an error when it is called with cert and key set but no protocol specified', function () {
+      // to do rewrite wrap function
+      (function () {
+        var c = mqtt.connect(sslOpts2)
+        c.end()
+      }).should.throw('Missing secure protocol key')
+    })
+
+    it('should throw an error when it is called with cert and key set and protocol other than allowed: mqtt,mqtts,ws,wss,wxs', function () {
+      (function () {
+        sslOpts2.protocol = 'UNKNOWNPROTOCOL'
+        var c = mqtt.connect(sslOpts2)
+        c.end()
+      }).should.throw()
+    })
+
+    it('should return a MqttClient with mqtts set when connect is called key and cert set and protocol mqtt', function () {
+      sslOpts2.protocol = 'mqtt'
+      var c = mqtt.connect(sslOpts2)
+
+      c.options.should.have.property('protocol', 'mqtts')
+
+      c.on('error', function () {})
+
+      c.should.be.instanceOf(mqtt.MqttClient)
+    })
+
+    it('should return a MqttClient with mqtts set when connect is called key and cert set and protocol mqtts', function () {
+      sslOpts2.protocol = 'mqtts'
+      var c = mqtt.connect(sslOpts2)
+
+      c.options.should.have.property('protocol', 'mqtts')
+
+      c.on('error', function () {})
+
+      c.should.be.instanceOf(mqtt.MqttClient)
+    })
+
+    it('should return a MqttClient with wss set when connect is called key and cert set and protocol ws', function () {
+      sslOpts2.protocol = 'ws'
+      var c = mqtt.connect(sslOpts2)
+
+      c.options.should.have.property('protocol', 'wss')
+
+      c.on('error', function () {})
+
+      c.should.be.instanceOf(mqtt.MqttClient)
+    })
+
+    it('should return a MqttClient with wss set when connect is called key and cert set and protocol wss', function () {
+      sslOpts2.protocol = 'wss'
+      var c = mqtt.connect(sslOpts2)
+
+      c.options.should.have.property('protocol', 'wss')
+
+      c.on('error', function () {})
+
+      c.should.be.instanceOf(mqtt.MqttClient)
+    })
+
+    it('should return an MqttClient with the clientid with option of clientId as empty string', function () {
+      var c = mqtt.connect('mqtt://localhost:1883', {
+        clientId: ''
+      })
+
+      c.should.be.instanceOf(mqtt.MqttClient)
+      c.options.should.have.property('clientId', '')
+    })
+
+    it('should return an MqttClient with the clientid with option of clientId empty', function () {
+      var c = mqtt.connect('mqtt://localhost:1883')
+
+      c.should.be.instanceOf(mqtt.MqttClient)
+      c.options.should.have.property('clientId')
+      c.end()
+    })
+
+    it('should return an MqttClient with the clientid with option of with specific clientId', function () {
+      var c = mqtt.connect('mqtt://localhost:1883', {
+        clientId: '123'
+      })
+
+      c.should.be.instanceOf(mqtt.MqttClient)
+      c.options.should.have.property('clientId', '123')
+      c.end()
+    })
+  })
+})

+ 9 - 0
uni_modules/mqtt/test/mqtt_store.js

@@ -0,0 +1,9 @@
+'use strict'
+
+var mqtt = require('../lib/connect')
+
+describe('store in lib/connect/index.js (webpack entry point)', function () {
+  it('should create store', function (done) {
+    done(null, new mqtt.Store())
+  })
+})

+ 157 - 0
uni_modules/mqtt/test/secure_client.js

@@ -0,0 +1,157 @@
+'use strict'
+
+var mqtt = require('..')
+var path = require('path')
+var abstractClientTests = require('./abstract_client')
+var fs = require('fs')
+var port = 9899
+var KEY = path.join(__dirname, 'helpers', 'tls-key.pem')
+var CERT = path.join(__dirname, 'helpers', 'tls-cert.pem')
+var WRONG_CERT = path.join(__dirname, 'helpers', 'wrong-cert.pem')
+var Server = require('./server')
+
+var server = new Server.SecureServer({
+  key: fs.readFileSync(KEY),
+  cert: fs.readFileSync(CERT)
+}, function (client) {
+  client.on('connect', function (packet) {
+    if (packet.clientId === 'invalid') {
+      client.connack({returnCode: 2})
+    } else {
+      server.emit('connect', client)
+      client.connack({returnCode: 0})
+    }
+  })
+
+  client.on('publish', function (packet) {
+    setImmediate(function () {
+      /* jshint -W027 */
+      /* eslint default-case:0 */
+      switch (packet.qos) {
+        case 0:
+          break
+        case 1:
+          client.puback(packet)
+          break
+        case 2:
+          client.pubrec(packet)
+          break
+      }
+      /* jshint +W027 */
+    })
+  })
+
+  client.on('pubrel', function (packet) {
+    client.pubcomp(packet)
+  })
+
+  client.on('pubrec', function (packet) {
+    client.pubrel(packet)
+  })
+
+  client.on('pubcomp', function () {
+    // Nothing to be done
+  })
+
+  client.on('subscribe', function (packet) {
+    client.suback({
+      messageId: packet.messageId,
+      granted: packet.subscriptions.map(function (e) {
+        return e.qos
+      })
+    })
+  })
+
+  client.on('unsubscribe', function (packet) {
+    client.unsuback(packet)
+  })
+
+  client.on('pingreq', function () {
+    client.pingresp()
+  })
+}).listen(port)
+
+describe('MqttSecureClient', function () {
+  var config = { protocol: 'mqtts', port: port, rejectUnauthorized: false }
+  abstractClientTests(server, config)
+
+  describe('with secure parameters', function () {
+    it('should validate successfully the CA', function (done) {
+      var client = mqtt.connect({
+        protocol: 'mqtts',
+        port: port,
+        ca: [fs.readFileSync(CERT)],
+        rejectUnauthorized: true
+      })
+
+      client.on('error', function (err) {
+        done(err)
+      })
+
+      server.once('connect', function () {
+        done()
+      })
+    })
+
+    it('should validate successfully the CA using URI', function (done) {
+      var client = mqtt.connect('mqtts://localhost:' + port, {
+        ca: [fs.readFileSync(CERT)],
+        rejectUnauthorized: true
+      })
+
+      client.on('error', function (err) {
+        done(err)
+      })
+
+      server.once('connect', function () {
+        done()
+      })
+    })
+
+    it('should validate successfully the CA using URI with path', function (done) {
+      var client = mqtt.connect('mqtts://localhost:' + port + '/', {
+        ca: [fs.readFileSync(CERT)],
+        rejectUnauthorized: true
+      })
+
+      client.on('error', function (err) {
+        done(err)
+      })
+
+      server.once('connect', function () {
+        done()
+      })
+    })
+
+    it('should validate unsuccessfully the CA', function (done) {
+      var client = mqtt.connect({
+        protocol: 'mqtts',
+        port: port,
+        ca: [fs.readFileSync(WRONG_CERT)],
+        rejectUnauthorized: true
+      })
+
+      client.once('error', function () {
+        done()
+        client.end()
+        client.on('error', function () {})
+      })
+    })
+
+    it('should emit close on TLS error', function (done) {
+      var client = mqtt.connect({
+        protocol: 'mqtts',
+        port: port,
+        ca: [fs.readFileSync(WRONG_CERT)],
+        rejectUnauthorized: true
+      })
+
+      client.on('error', function () {})
+
+      // TODO node v0.8.x emits multiple close events
+      client.once('close', function () {
+        done()
+      })
+    })
+  })
+})

+ 93 - 0
uni_modules/mqtt/test/server.js

@@ -0,0 +1,93 @@
+'use strict'
+
+var net = require('net')
+var tls = require('tls')
+var inherits = require('inherits')
+var Connection = require('mqtt-connection')
+var MqttServer
+var FastMqttServer
+var MqttSecureServer
+
+function setupConnection (duplex) {
+  var that = this
+  var connection = new Connection(duplex, function () {
+    that.emit('client', connection)
+  })
+}
+
+/*
+ * MqttServer
+ *
+ * @param {Function} listener - fired on client connection
+ */
+MqttServer = module.exports = function Server (listener) {
+  if (!(this instanceof Server)) {
+    return new Server(listener)
+  }
+
+  net.Server.call(this)
+
+  this.on('connection', setupConnection)
+
+  if (listener) {
+    this.on('client', listener)
+  }
+
+  return this
+}
+inherits(MqttServer, net.Server)
+
+/*
+ * FastMqttServer(w/o waiting for initialization)
+ *
+ * @param {Function} listener - fired on client connection
+ */
+FastMqttServer = module.exports.FastMqttServer = function Server (listener) {
+  if (!(this instanceof Server)) {
+    return new Server(listener)
+  }
+
+  net.Server.call(this)
+
+  this.on('connection', function (duplex) {
+    var connection = new Connection(duplex)
+    this.emit('client', connection)
+  })
+
+  if (listener) {
+    this.on('client', listener)
+  }
+
+  return this
+}
+inherits(FastMqttServer, net.Server)
+
+/**
+ * MqttSecureServer
+ *
+ * @param {Object} opts - server options
+ * @param {Function} listener
+ */
+MqttSecureServer = module.exports.SecureServer =
+  function SecureServer (opts, listener) {
+    if (!(this instanceof SecureServer)) {
+      return new SecureServer(opts, listener)
+    }
+
+    // new MqttSecureServer(function(){})
+    if (typeof opts === 'function') {
+      listener = opts
+      opts = {}
+    }
+
+    tls.Server.call(this, opts)
+
+    if (listener) {
+      this.on('client', listener)
+    }
+
+    this.on('secureConnection', setupConnection)
+
+    return this
+  }
+inherits(MqttSecureServer, tls.Server)

+ 10 - 0
uni_modules/mqtt/test/store.js

@@ -0,0 +1,10 @@
+'use strict'
+
+var Store = require('../lib/store')
+var abstractTest = require('../test/abstract_store')
+
+describe('in-memory store', function () {
+  abstractTest(function (done) {
+    done(null, new Store())
+  })
+})

+ 22 - 0
uni_modules/mqtt/test/typescript/broker-connect-subscribe-and-publish.ts

@@ -0,0 +1,22 @@
+// relative path uses package.json {"types":"types/index.d.ts", ...}
+import {IClientOptions, Client, connect, IConnackPacket} from '../..'
+const BROKER = 'test.mosquitto.org'
+
+const PAYLOAD = 'hello from TS'
+const TOPIC = 'typescript-test-' + Math.random().toString(16).substr(2)
+const opts: IClientOptions = {}
+
+console.log(`connect(${JSON.stringify(BROKER)})`)
+const client:Client = connect(`mqtt://${BROKER}`, opts)
+
+client.subscribe({[TOPIC]: {qos: 2}}, (err, granted) => {
+    granted.forEach(({topic, qos}) => {
+        console.log(`subscribed to ${topic} with qos=${qos}`)
+    })
+    client.publish(TOPIC, PAYLOAD, {qos: 2})
+}).on('message', (topic: string, payload: Buffer) => {
+    console.log(`message from ${topic}: ${payload}`)
+    client.end()
+}).on('connect', (packet: IConnackPacket) => {
+    console.log('connected!', JSON.stringify(packet))
+})

+ 14 - 0
uni_modules/mqtt/test/typescript/tsconfig.json

@@ -0,0 +1,14 @@
+{
+  "compilerOptions": {
+    "module": "commonjs",
+    "target": "es5",
+    "moduleResolution": "node",
+    "noEmitOnError": true,
+    "noImplicitAny": true,
+    "alwaysStrict": true,
+    "strictNullChecks": true,
+    "noImplicitReturns": true,
+    "noImplicitThis": true,
+    "sourceMap": true
+  }
+}

+ 13 - 0
uni_modules/mqtt/test/util.js

@@ -0,0 +1,13 @@
+'use strict'
+
+var through = require('through2')
+
+module.exports.testStream = function () {
+  return through(function (buf, enc, cb) {
+    var that = this
+    setImmediate(function () {
+      that.push(buf)
+      cb()
+    })
+  })
+}

+ 144 - 0
uni_modules/mqtt/test/websocket_client.js

@@ -0,0 +1,144 @@
+'use strict'
+
+var http = require('http')
+var websocket = require('websocket-stream')
+var WebSocketServer = require('ws').Server
+var Connection = require('mqtt-connection')
+var abstractClientTests = require('./abstract_client')
+var mqtt = require('../')
+var xtend = require('xtend')
+var assert = require('assert')
+var port = 9999
+var server = http.createServer()
+
+function attachWebsocketServer (wsServer) {
+  var wss = new WebSocketServer({server: wsServer, perMessageDeflate: false})
+
+  wss.on('connection', function (ws) {
+    var stream = websocket(ws)
+    var connection = new Connection(stream)
+
+    wsServer.emit('client', connection)
+    stream.on('error', function () {})
+    connection.on('error', function () {})
+  })
+
+  return wsServer
+}
+
+attachWebsocketServer(server)
+
+server.on('client', function (client) {
+  client.on('connect', function (packet) {
+    if (packet.clientId === 'invalid') {
+      client.connack({ returnCode: 2 })
+    } else {
+      server.emit('connect', client)
+      client.connack({returnCode: 0})
+    }
+  })
+
+  client.on('publish', function (packet) {
+    setImmediate(function () {
+      switch (packet.qos) {
+        case 0:
+          break
+        case 1:
+          client.puback(packet)
+          break
+        case 2:
+          client.pubrec(packet)
+          break
+      }
+    })
+  })
+
+  client.on('pubrel', function (packet) {
+    client.pubcomp(packet)
+  })
+
+  client.on('pubrec', function (packet) {
+    client.pubrel(packet)
+  })
+
+  client.on('pubcomp', function () {
+    // Nothing to be done
+  })
+
+  client.on('subscribe', function (packet) {
+    client.suback({
+      messageId: packet.messageId,
+      granted: packet.subscriptions.map(function (e) {
+        return e.qos
+      })
+    })
+  })
+
+  client.on('unsubscribe', function (packet) {
+    client.unsuback(packet)
+  })
+
+  client.on('pingreq', function () {
+    client.pingresp()
+  })
+}).listen(port)
+
+describe('Websocket Client', function () {
+  var baseConfig = { protocol: 'ws', port: port }
+
+  function makeOptions (custom) {
+    // xtend returns a new object. Does not mutate arguments
+    return xtend(baseConfig, custom || {})
+  }
+
+  it('should use mqtt as the protocol by default', function (done) {
+    server.once('client', function (client) {
+      client.stream.socket.protocol.should.equal('mqtt')
+    })
+    mqtt.connect(makeOptions()).on('connect', function () {
+      this.end(true, done)
+    })
+  })
+
+  it('should be able transform the url (for e.g. to sign it)', function (done) {
+    var baseUrl = 'ws://localhost:9999/mqtt'
+    var sig = '?AUTH=token'
+    var expected = baseUrl + sig
+    var actual
+    var opts = makeOptions({
+      path: '/mqtt',
+      transformWsUrl: function (url, opt, client) {
+        assert.equal(url, baseUrl)
+        assert.strictEqual(opt, opts)
+        assert.strictEqual(client.options, opts)
+        assert.strictEqual(typeof opt.transformWsUrl, 'function')
+        assert(client instanceof mqtt.MqttClient)
+        url += sig
+        actual = url
+        return url
+      }})
+    mqtt.connect(opts)
+      .on('connect', function () {
+        assert.equal(this.stream.socket.url, expected)
+        assert.equal(actual, expected)
+        this.end(true, done)
+      })
+  })
+
+  it('should use mqttv3.1 as the protocol if using v3.1', function (done) {
+    server.once('client', function (client) {
+      client.stream.socket.protocol.should.equal('mqttv3.1')
+    })
+
+    var opts = makeOptions({
+      protocolId: 'MQIsdp',
+      protocolVersion: 3
+    })
+
+    mqtt.connect(opts).on('connect', function () {
+      this.end(true, done)
+    })
+  })
+
+  abstractClientTests(server, makeOptions())
+})

+ 27 - 0
uni_modules/mqtt/types/index.d.ts

@@ -0,0 +1,27 @@
+export * from './lib/client'
+export * from './lib/connect'
+export * from './lib/store'
+export * from './lib/client-options'
+import { MqttClient } from './lib/client'
+export { MqttClient as Client }
+export {
+  QoS,
+  PacketCmd,
+  IPacket,
+  IConnectPacket,
+  IPublishPacket,
+  IConnackPacket,
+  ISubscription,
+  ISubscribePacket,
+  ISubackPacket,
+  IUnsubscribePacket,
+  IUnsubackPacket,
+  IPubackPacket,
+  IPubcompPacket,
+  IPubrelPacket,
+  IPubrecPacket,
+  IPingreqPacket,
+  IPingrespPacket,
+  IDisconnectPacket,
+  Packet
+} from 'mqtt-packet'

+ 178 - 0
uni_modules/mqtt/types/lib/client-options.d.ts

@@ -0,0 +1,178 @@
+import { MqttClient } from './client'
+import { Store } from './store'
+import { QoS } from 'mqtt-packet'
+
+export declare type StorePutCallback = () => void
+
+export interface IClientOptions extends ISecureClientOptions {
+  port?: number // port is made into a number subsequently
+  host?: string // host does NOT include port
+  hostname?: string
+  path?: string
+  protocol?: 'wss' | 'ws' | 'mqtt' | 'mqtts' | 'tcp' | 'ssl' | 'wx' | 'wxs'
+
+  wsOptions?: {
+    [x: string]: any
+  }
+  /**
+   *  10 seconds, set to 0 to disable
+   */
+  keepalive?: number
+  /**
+   * 'mqttjs_' + Math.random().toString(16).substr(2, 8)
+   */
+  clientId?: string
+  /**
+   * 'MQTT'
+   */
+  protocolId?: string
+  /**
+   * 4
+   */
+  protocolVersion?: number
+  /**
+   * true, set to false to receive QoS 1 and 2 messages while offline
+   */
+  clean?: boolean
+  /**
+   * 1000 milliseconds, interval between two reconnections
+   */
+  reconnectPeriod?: number
+  /**
+   * 30 * 1000 milliseconds, time to wait before a CONNACK is received
+   */
+  connectTimeout?: number
+  /**
+   * the username required by your broker, if any
+   */
+  username?: string
+  /**
+   * the password required by your broker, if any
+   */
+  password?: string
+  /**
+   * a Store for the incoming packets
+   */
+  incomingStore?: Store
+  /**
+   * a Store for the outgoing packets
+   */
+  outgoingStore?: Store
+  queueQoSZero?: boolean
+  reschedulePings?: boolean
+  servers?: Array<{
+    host: string
+    port: number
+    protocol?: 'wss' | 'ws' | 'mqtt' | 'mqtts' | 'tcp' | 'ssl' | 'wx' | 'wxs'
+  }>
+  /**
+   * true, set to false to disable re-subscribe functionality
+   */
+  resubscribe?: boolean
+  /**
+   * a message that will sent by the broker automatically when the client disconnect badly.
+   */
+  will?: {
+    /**
+     * the topic to publish
+     */
+    topic: string
+    /**
+     * the message to publish
+     */
+    payload: string
+    /**
+     * the QoS
+     */
+    qos: QoS
+    /**
+     * the retain flag
+     */
+    retain: boolean,
+    /*
+    *  properies object of will
+    * */
+    properties?: {
+      willDelayInterval?: number,
+      payloadFormatIndicator?: number,
+      messageExpiryInterval?: number,
+      contentType?: string,
+      responseTopic?: string,
+      correlationData?: Buffer,
+      userProperties?: Object
+    }
+  }
+  transformWsUrl?: (url: string, options: IClientOptions, client: MqttClient) => string,
+  properties?: {
+    sessionExpiryInterval?: number,
+    receiveMaximum?: number,
+    maximumPacketSize?: number,
+    topicAliasMaximum?: number,
+    requestResponseInformation?: boolean,
+    requestProblemInformation?: boolean,
+    userProperties?: Object,
+    authenticationMethod?: string,
+    authenticationData?: Buffer
+  }
+}
+export interface ISecureClientOptions {
+  /**
+   * optional private keys in PEM format
+   */
+  key?: string | string[] | Buffer | Buffer[] | Object[]
+  /**
+   * optional cert chains in PEM format
+   */
+  cert?: string | string[] | Buffer | Buffer[]
+  /**
+   * Optionally override the trusted CA certificates in PEM format
+   */
+  ca?: string | string[] | Buffer | Buffer[]
+  rejectUnauthorized?: boolean
+}
+export interface IClientPublishOptions {
+  /**
+   * the QoS
+   */
+  qos: QoS
+  /**
+   * the retain flag
+   */
+  retain?: boolean
+  /**
+   * whether or not mark a message as duplicate
+   */
+  dup?: boolean
+  /**
+   * callback called when message is put into `outgoingStore`
+   */
+  cbStorePut?: StorePutCallback
+}
+export interface IClientSubscribeOptions {
+  /**
+   * the QoS
+   */
+  qos: QoS,
+  /*
+  * no local flag
+  * */
+  nl?: boolean,
+  /*
+  * Retain As Published flag
+  * */
+  rap?: boolean,
+  /*
+  * Retain Handling option
+  * */
+  rh?: number
+}
+export interface IClientReconnectOptions {
+  /**
+   * a Store for the incoming packets
+   */
+  incomingStore?: Store
+  /**
+   * a Store for the outgoing packets
+   */
+  outgoingStore?: Store
+}

+ 231 - 0
uni_modules/mqtt/types/lib/client.d.ts

@@ -0,0 +1,231 @@
+/// <reference types="node" />
+
+import * as events from 'events'
+import {
+  IClientOptions,
+  IClientPublishOptions,
+  IClientSubscribeOptions,
+  IClientReconnectOptions
+} from './client-options'
+import { Store } from './store'
+import { Packet, QoS } from 'mqtt-packet'
+
+export interface ISubscriptionGrant {
+  /**
+   *  is a subscribed to topic
+   */
+  topic: string
+  /**
+   *  is the granted qos level on it, may return 128 on error
+   */
+  qos: QoS | number
+  /*
+  * no local flag
+  * */
+  nl?: boolean,
+  /*
+  * Retain As Published flag
+  * */
+  rap?: boolean,
+  /*
+  * Retain Handling option
+  * */
+  rh?: number
+}
+export interface ISubscriptionRequest {
+  /**
+   *  is a subscribed to topic
+   */
+  topic: string
+  /**
+   *  is the granted qos level on it
+   */
+  qos: QoS
+  /*
+  * no local flag
+  * */
+  nl?: boolean,
+  /*
+  * Retain As Published flag
+  * */
+  rap?: boolean,
+  /*
+  * Retain Handling option
+  * */
+  rh?: number
+}
+export interface ISubscriptionMap {
+  /**
+   * object which has topic names as object keys and as value the options, like {'test1': {qos: 0}, 'test2': {qos: 2}}.
+   */
+  [topic: string]: {
+    qos: QoS,
+    nl?: boolean,
+    rap?: boolean,
+    rh?: number
+  }
+}
+
+export declare type ClientSubscribeCallback = (err: Error, granted: ISubscriptionGrant[]) => void
+export declare type OnMessageCallback = (topic: string, payload: Buffer, packet: Packet) => void
+export declare type OnPacketCallback = (packet: Packet) => void
+export declare type OnErrorCallback = (error: Error) => void
+export declare type PacketCallback = (error?: Error, packet?: Packet) => any
+export declare type CloseCallback = () => void
+
+export interface IStream extends events.EventEmitter {
+  pipe (to: any): any
+  destroy (): any
+  end (): any
+}
+/**
+ * MqttClient constructor
+ *
+ * @param {Stream} stream - stream
+ * @param {Object} [options] - connection options
+ * (see Connection#connect)
+ */
+export declare class MqttClient extends events.EventEmitter {
+  public connected: boolean
+  public disconnecting: boolean
+  public disconnected: boolean
+  public reconnecting: boolean
+  public incomingStore: Store
+  public outgoingStore: Store
+  public options: IClientOptions
+  public queueQoSZero: boolean
+
+  constructor (streamBuilder: (client: MqttClient) => IStream, options: IClientOptions)
+
+  public on (event: 'message', cb: OnMessageCallback): this
+  public on (event: 'packetsend' | 'packetreceive', cb: OnPacketCallback): this
+  public on (event: 'error', cb: OnErrorCallback): this
+  public on (event: string, cb: Function): this
+
+  public once (event: 'message', cb: OnMessageCallback): this
+  public once (event:
+                'packetsend'
+                | 'packetreceive', cb: OnPacketCallback): this
+  public once (event: 'error', cb: OnErrorCallback): this
+  public once (event: string, cb: Function): this
+
+  /**
+   * publish - publish <message> to <topic>
+   *
+   * @param {String} topic - topic to publish to
+   * @param {(String|Buffer)} message - message to publish
+   *
+   * @param {Object}    [opts] - publish options, includes:
+   *   @param {Number}  [opts.qos] - qos level to publish on
+   *   @param {Boolean} [opts.retain] - whether or not to retain the message
+   *   @param {Function}[opts.cbStorePut] - function(){}
+   *       called when message is put into `outgoingStore`
+   *
+   * @param {Function} [callback] - function(err){}
+   *    called when publish succeeds or fails
+   *
+   * @returns {Client} this - for chaining
+   * @api public
+   *
+   * @example client.publish('topic', 'message')
+   * @example
+   *     client.publish('topic', 'message', {qos: 1, retain: true})
+   * @example client.publish('topic', 'message', console.log)
+   */
+  public publish (topic: string, message: string | Buffer,
+                 opts: IClientPublishOptions, callback?: PacketCallback): this
+  public publish (topic: string, message: string | Buffer,
+                 callback?: PacketCallback): this
+
+  /**
+   * subscribe - subscribe to <topic>
+   *
+   * @param {String, Array, Object} topic - topic(s) to subscribe to, supports objects in the form {'topic': qos}
+   * @param {Object} [opts] - optional subscription options, includes:
+   * @param  {Number} [opts.qos] - subscribe qos level
+   * @param {Function} [callback] - function(err, granted){} where:
+   *    {Error} err - subscription error (none at the moment!)
+   *    {Array} granted - array of {topic: 't', qos: 0}
+   * @returns {MqttClient} this - for chaining
+   * @api public
+   * @example client.subscribe('topic')
+   * @example client.subscribe('topic', {qos: 1})
+   * @example client.subscribe({'topic': 0, 'topic2': 1}, console.log)
+   * @example client.subscribe('topic', console.log)
+   */
+  public subscribe (topic:
+                     string
+                     | string[], opts: IClientSubscribeOptions, callback?: ClientSubscribeCallback): this
+  public subscribe (topic:
+                     string
+                     | string[]
+                     | ISubscriptionMap, callback?: ClientSubscribeCallback): this
+
+  /**
+   * unsubscribe - unsubscribe from topic(s)
+   *
+   * @param {String, Array} topic - topics to unsubscribe from
+   * @param {Object} opts - opts of unsubscribe
+   * @param {Function} [callback] - callback fired on unsuback
+   * @returns {MqttClient} this - for chaining
+   * @api public
+   * @example client.unsubscribe('topic')
+   * @example client.unsubscribe('topic', console.log)
+   * @example client.unsubscribe('topic', opts, console.log)
+   */
+  public unsubscribe (topic: string | string[], opts?: Object, callback?: PacketCallback): this
+
+  /**
+   * end - close connection
+   *
+   * @returns {MqttClient} this - for chaining
+   * @param {Boolean} force - do not wait for all in-flight messages to be acked
+   * @param {Object} opts - opts disconnect
+   * @param {Function} cb - called when the client has been closed
+   *
+   * @api public
+   */
+  public end (force?: boolean, opts?: Object, cb?: CloseCallback): this
+
+  /**
+   * removeOutgoingMessage - remove a message in outgoing store
+   * the outgoing callback will be called withe Error('Message removed') if the message is removed
+   *
+   * @param {Number} mid - messageId to remove message
+   * @returns {MqttClient} this - for chaining
+   * @api public
+   *
+   * @example client.removeOutgoingMessage(client.getLastMessageId());
+   */
+  public removeOutgoingMessage (mid: number): this
+
+  /**
+   * reconnect - connect again using the same options as connect()
+   *
+   * @param {Object} [opts] - optional reconnect options, includes:
+   *    {Store} incomingStore - a store for the incoming packets
+   *    {Store} outgoingStore - a store for the outgoing packets
+   *    if opts is not given, current stores are used
+   *
+   * @returns {MqttClient} this - for chaining
+   *
+   * @api public
+   */
+  public reconnect (opts?: IClientReconnectOptions): this
+
+  /**
+   * Handle messages with backpressure support, one at a time.
+   * Override at will.
+   *
+   * @param packet packet the packet
+   * @param callback callback call when finished
+   * @api public
+   */
+  public handleMessage (packet: Packet, callback: PacketCallback): void
+
+  /**
+   * getLastMessageId
+   */
+  public getLastMessageId (): number
+}
+export { IClientOptions }

+ 10 - 0
uni_modules/mqtt/types/lib/connect/index.d.ts

@@ -0,0 +1,10 @@
+import { IClientOptions, MqttClient } from '../client'
+/**
+ * connect - connect to an MQTT broker.
+ *
+ * @param {String} [brokerUrl] - url of the broker, optional
+ * @param {Object} opts - see MqttClient#constructor
+ */
+declare function connect (brokerUrl?: string | any, opts?: IClientOptions): MqttClient
+export { connect }
+export { MqttClient }

+ 6 - 0
uni_modules/mqtt/types/lib/store-options.d.ts

@@ -0,0 +1,6 @@
+export interface IStoreOptions {
+  /**
+   * true, clear _inflights at close
+   */
+  clean?: boolean
+}

+ 0 - 0
uni_modules/mqtt/types/lib/store.d.ts


Vissa filer visades inte eftersom för många filer har ändrats