mqtt.js 4.8 KB

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