mqtt.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. import mqtt from 'mqtt'
  2. import SM4 from '@/utils/sm4'
  3. const willTopic = 'web/offline'
  4. const sm4 = new SM4({
  5. // encrypt/decypt main key; cannot be omitted
  6. // key: 'fELIMxLsdoSgRZnX',
  7. key: [102, 69, 76, 73, 77, 120, 76, 115, 100, 111, 83, 103, 82, 90, 110, 88],
  8. // optional; can be 'cbc' or 'ecb'
  9. // default is ecb
  10. mode: 'ecb',
  11. // optional; can be 'uint8array' or 'text'
  12. // default is uint8array
  13. outType: 'uint8array'
  14. })
  15. const host = process.env.VUE_APP_MQTT_URL || `${location.protocol === 'https:' ? 'wss' : 'ws'}://${location.host}/mqtt`
  16. const username = process.env.VUE_APP_MQTT_USER_NAME
  17. const password = process.env.VUE_APP_MQTT_PASSWORD
  18. const options = {
  19. username,
  20. password,
  21. clientId: 'mqttjs_' + Math.random().toString(16).slice(2),
  22. keepalive: 60,
  23. protocolId: 'MQTT',
  24. protocolVersion: 4,
  25. clean: true,
  26. reconnectPeriod: 1000,
  27. connectTimeout: 30 * 1000,
  28. will: {
  29. topic: willTopic,
  30. payload: 'Connection Closed abnormally..!',
  31. qos: 0,
  32. retain: false
  33. }
  34. }
  35. const whiteList = [
  36. {
  37. type: 'topic',
  38. val: /^\$SYS/
  39. }, {
  40. type: 'topic',
  41. eq: true,
  42. val: willTopic
  43. }, {
  44. type: 'topic',
  45. val: /^\d+\/\d+\/(status|screenshot)/
  46. }, {
  47. val: /messageId/
  48. }
  49. ]
  50. function isWhite (item, topic, message) {
  51. switch (item.type) {
  52. case 'topic':
  53. return item.eq ? topic === item.val : item.val.test(topic)
  54. default:
  55. return item.val.test(message)
  56. }
  57. }
  58. let mqttClient
  59. function changeState (state) {
  60. mqttClient && (mqttClient.state = state)
  61. }
  62. function start () {
  63. console.log('Connecting mqtt client...')
  64. const client = mqtt.connect(host, options)
  65. client.on('error', err => {
  66. console.log('MQTT connection error: ', err)
  67. client.end(true)
  68. mqttClient = null
  69. })
  70. client.on('connect', () => {
  71. console.log('MQTT connected')
  72. changeState('connect')
  73. })
  74. client.on('reconnect', () => {
  75. console.log('MQTT reconnecting...')
  76. changeState('reconnect')
  77. })
  78. client.on('close', () => {
  79. console.log('MQTT closed')
  80. changeState('close')
  81. })
  82. client.on('disconnect', () => {
  83. console.log('MQTT disconnect')
  84. changeState('disconnect')
  85. })
  86. client.on('offline', () => {
  87. console.log('MQTT offline')
  88. changeState('offline')
  89. })
  90. client.on('end', () => {
  91. console.log('MQTT end')
  92. mqttClient = null
  93. })
  94. client.on('message', (topic, message) => {
  95. console.log('MQTT topic', topic)
  96. try {
  97. const s = utf8ArrayBufferToString(message)
  98. if (s) {
  99. if (whiteList.some(item => isWhite(item, topic, s))) {
  100. console.log('white list')
  101. message = s
  102. } else {
  103. const decodeStr = sm4.decrypt(message)
  104. if (decodeStr) {
  105. console.log('sm4')
  106. message = decodeStr
  107. } else {
  108. console.log('unencrypted')
  109. message = s
  110. }
  111. }
  112. } else {
  113. console.log('null')
  114. message = s
  115. }
  116. } catch (e) {
  117. console.warn(e)
  118. message = utf8ArrayBufferToString(message)
  119. }
  120. console.log(message)
  121. cbs.forEach(cb => cb(topic, message))
  122. })
  123. return client
  124. }
  125. const decoder = new TextDecoder('utf-8')
  126. function utf8ArrayBufferToString (buffer) {
  127. return decoder.decode(buffer)
  128. }
  129. function inst () {
  130. if (!mqttClient) {
  131. mqttClient = {
  132. state: 'init',
  133. client: start()
  134. }
  135. }
  136. return mqttClient
  137. }
  138. export function close () {
  139. if (mqttClient) {
  140. mqttClient.client.end(true)
  141. mqttClient = null
  142. }
  143. }
  144. export function subscribe (topic, fn) {
  145. if (topic) {
  146. console.info(`MQTT subscribe ${topic}`)
  147. inst().client.subscribe(topic, e => {
  148. e && console.warn('MQTT subscribe', e)
  149. })
  150. }
  151. listen(fn)
  152. }
  153. export function unsubscribe (topic, fn) {
  154. if (topic && mqttClient) {
  155. console.info(`MQTT unsubscribe ${topic}`)
  156. mqttClient.client.unsubscribe(topic, e => {
  157. e && console.warn('MQTT unsubscribe', e)
  158. })
  159. }
  160. unlisten(fn)
  161. }
  162. const cbs = []
  163. export function listen (fn) {
  164. fn && cbs.push(fn)
  165. }
  166. export function unlisten (fn) {
  167. if (fn) {
  168. const index = cbs.findIndex(item => item === fn)
  169. if (~index) {
  170. cbs.splice(index, 1)
  171. }
  172. }
  173. }
  174. export function publish (topic, message, encode) {
  175. return new Promise((resolve, reject) => {
  176. const mqttInst = inst()
  177. if (mqttInst && mqttInst.state === 'connect') {
  178. mqttInst.client.publish(topic, message && encode ? sm4.encrypt(message) : message, err => {
  179. if (err) {
  180. console.warn(err)
  181. reject(err)
  182. } else {
  183. resolve()
  184. }
  185. })
  186. } else {
  187. reject('not connected')
  188. }
  189. })
  190. }