├── .editorconfig ├── .gitignore ├── README.md ├── mqtt-msg.js ├── mqtt-store.js ├── mqtt.js └── package.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | indent_style = space 11 | indent_size = 2 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IJ 26 | # 27 | .idea 28 | .gradle 29 | local.properties 30 | 31 | # node.js 32 | # 33 | node_modules/ 34 | npm-debug.log 35 | 36 | # BUCK 37 | buck-out/ 38 | \.buckd/ 39 | android/app/libs 40 | android/keystores/debug.keystore 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-mqttjs 2 | 3 | ## Features 4 | 5 | This is 100% Pure javascript Websocket MQTT client library without any dependencies for [react-native](https://facebook.github.io/react-native), use original react-native Websocket, port from: [MQTT client library for Contiki](https://github.com/esar/contiki-mqtt) - Prefer for any IoT Mobile applications using react-native 6 | 7 | Support subscribing, publishing, authentication, will messages, keep alive pings and all 3 QoS levels (it should be a fully functional client). 8 | 9 | Sampe as [esp_mqtt for ESP8266 chip](https://github.com/tuanpmt/esp_mqtt) and should replace for [react-native-mqtt](https://github.com/tuanpmt/react-native-mqtt) 10 | 11 | ## Install 12 | 13 | `npm i react-native-mqttjs --save` 14 | 15 | ## Basic usage 16 | 17 | ```javascript 18 | import { MqttClient } from 'react-native-mqttjs'; 19 | 20 | var mqtt = new MqttClient(); 21 | mqtt.connect('ws://test.mosquitto.org:8080'); 22 | 23 | mqtt.subscribe({'/topic1': 0}, () => { 24 | console.log('subscribe success topic1 with qos 0'); 25 | }); 26 | mqtt.subscribe([{'/topic2': 1}, {'/topic3': 2}], () => { 27 | console.log('subscribe success topic2 with qos 1, topic3 with qos 2'); 28 | }); 29 | 30 | mqtt.publish('/topic1', 'test', {qos: 0, retain: 0}, () => { 31 | console.log('publish success'); 32 | }); 33 | 34 | mqtt.addListener('message', (topic, data) => { 35 | console.log('data arrived, topic: ' + topic + ' - data: ' + data); 36 | }); 37 | 38 | mqtt.addListener('connect', () => { 39 | console.log('mqtt connected'); 40 | }); 41 | 42 | ``` 43 | 44 | ## API 45 | 46 | - Options: 47 | + keepalive: 120 seconds 48 | + reschedulePings: reschedule ping messages after sending packets (default true) 49 | + clientId: 'mqttjs_' + Math.random().toString(16).substr(2, 8) 50 | + protocolId: 'MQTT' or 'MQIsdp' 51 | + clean: true, set to false to receive QoS 1 and 2 messages while offline 52 | + reconnectPeriod: 1000 milliseconds, interval between two reconnections 53 | + connectTimeout: 30 * 1000 milliseconds, time to wait before a CONNACK is received 54 | + username: the username required by your broker, if any 55 | + password: the password required by your broker, if any 56 | + will: a message that will sent by the broker automatically when the client disconnect badly. The format is: 57 | - topic: the topic to publish 58 | - payload: the message to publish 59 | - qos: the QoS 60 | - retain: the retain flag 61 | 62 | ## License 63 | 64 | MIT 65 | -------------------------------------------------------------------------------- /mqtt-msg.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: TuanPM 3 | * @Date: 2016-05-24 14:56:01 4 | * @Last Modified by: TuanPM 5 | * @Last Modified time: 2016-05-26 11:26:49 6 | */ 7 | 8 | 'use strict'; 9 | 10 | const mqtt_msg_type = 11 | { 12 | MQTT_MSG_TYPE_CONNECT : 1, 13 | MQTT_MSG_TYPE_CONNACK : 2, 14 | MQTT_MSG_TYPE_PUBLISH : 3, 15 | MQTT_MSG_TYPE_PUBACK : 4, 16 | MQTT_MSG_TYPE_PUBREC : 5, 17 | MQTT_MSG_TYPE_PUBREL : 6, 18 | MQTT_MSG_TYPE_PUBCOMP : 7, 19 | MQTT_MSG_TYPE_SUBSCRIBE : 8, 20 | MQTT_MSG_TYPE_SUBACK : 9, 21 | MQTT_MSG_TYPE_UNSUBSCRIBE: 10, 22 | MQTT_MSG_TYPE_UNSUBACK: 11, 23 | MQTT_MSG_TYPE_PINGREQ: 12, 24 | MQTT_MSG_TYPE_PINGRESP: 13, 25 | MQTT_MSG_TYPE_DISCONNECT:14 26 | }; 27 | const mqtt_connect_flag = 28 | { 29 | MQTT_CONNECT_FLAG_USERNAME : 1 << 7, 30 | MQTT_CONNECT_FLAG_PASSWORD : 1 << 6, 31 | MQTT_CONNECT_FLAG_WILL_RETAIN : 1 << 5, 32 | MQTT_CONNECT_FLAG_WILL : 1 << 2, 33 | MQTT_CONNECT_FLAG_CLEAN_SESSION : 1 << 1 34 | }; 35 | 36 | Array.prototype.push_len = function(data) { 37 | this.push((data & 0xFFFF) >> 8); 38 | this.push(data & 0xFF); 39 | } 40 | 41 | Array.prototype.push_str = function(str) { 42 | let self = this; 43 | this.push((str.length & 0xFFFF) >> 8); 44 | this.push(str.length & 0xFF); 45 | str.split('').map(c => self.push(c.charCodeAt(0))); 46 | } 47 | 48 | Array.prototype.push_array = function(arr) { 49 | let self = this; 50 | this.push((str.length & 0xFFFF) >> 8); 51 | this.push(str.length & 0xFF); 52 | arr.map(c => self.push(c)); 53 | } 54 | 55 | Array.prototype.to_str = function() { 56 | let arr = this; 57 | let str = ''; 58 | arr.map((c) => { 59 | str += String.fromCharCode(c); 60 | }); 61 | return str; 62 | } 63 | 64 | var encodeLen = (len) => { 65 | var ret = []; 66 | 67 | do { 68 | let encodeByte = len%128; 69 | len = Math.trunc(len/128); 70 | if(len > 0) { 71 | encodeByte |= 128; 72 | } 73 | ret.push(encodeByte); 74 | } while(len > 0); 75 | return ret; 76 | } 77 | 78 | var mqtt_fixed_header = function(len, type, dup, qos, retain) { 79 | let fixed_header = []; 80 | fixed_header.push( ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1) ); 81 | return fixed_header.concat(encodeLen(len)); 82 | } 83 | 84 | var mqtt_msg_connect = function(info) { 85 | 86 | 87 | let connection_info = { 88 | protocolId: 'MQTT', //MQIsdp 89 | clientId: 'mqttjs_' + Math.random().toString(16).substr(2, 8), 90 | clean: true, 91 | connectTimeout: 30000, 92 | keepalive: 120, 93 | username: null, 94 | password: null, 95 | will: null, 96 | }; 97 | 98 | let payload = []; 99 | let variable_header = []; //protocolLen 100 | 101 | for(let info_key in connection_info) { 102 | //console.log(info_key); 103 | info[info_key] && (connection_info[info_key] = info[info_key]); 104 | } 105 | 106 | if(connection_info.protocolId != 'MQTT' && connection_info.protocolId != 'MQIsdp') 107 | return null; 108 | 109 | 110 | variable_header.push_str(connection_info.protocolId); 111 | variable_header.push(connection_info.protocolId === 'MQTT'? 4: 3); //level 112 | 113 | let flags = 0; 114 | if(connection_info.clean) 115 | flags |= mqtt_connect_flag.MQTT_CONNECT_FLAG_CLEAN_SESSION; 116 | 117 | if(connection_info.clientId != null) { 118 | payload.push_str(connection_info.clientId); 119 | } 120 | 121 | if(connection_info.will && connection_info.will.willTopic && connection_info.will.willMsg) { 122 | 123 | payload.push_str(connection_info.will.willTopic); 124 | payload.push_str(connection_info.will.willMsg); 125 | 126 | flags |= mqtt_connect_flag.MQTT_CONNECT_FLAG_WILL; 127 | if(connection_info.will.willRetain) 128 | flags |= mqtt_connect_flag.MQTT_CONNECT_FLAG_WILL_RETAIN; 129 | 130 | flags |= (connection_info.will.willQos & 3) << 4; 131 | } 132 | 133 | if(connection_info.username != null) { 134 | payload.push_str(connection_info.username) 135 | flags |= mqtt_connect_flag.MQTT_CONNECT_FLAG_USERNAME; 136 | } 137 | 138 | if(connection_info.password != null) { 139 | payload.push_str(connection_info.password) 140 | 141 | flags |= mqtt_connect_flag.MQTT_CONNECT_FLAG_PASSWORD; 142 | } 143 | 144 | variable_header.push(flags); //flag 145 | 146 | variable_header.push_len(connection_info.keepalive); 147 | 148 | let totalLen = variable_header.length + payload.length; 149 | 150 | let fixed_header = mqtt_fixed_header(totalLen, mqtt_msg_type.MQTT_MSG_TYPE_CONNECT, 0, 0, 0); 151 | 152 | return fixed_header.concat(variable_header, payload); 153 | 154 | } 155 | 156 | var mqtt_get_publish_topic = function(buffer) 157 | { 158 | let topiclen; 159 | let totlen = 0; 160 | let i; 161 | for(i = 1; i < buffer.length; ++i) 162 | { 163 | totlen += (buffer[i] & 0x7f) << (7 * (i -1)); 164 | if((buffer[i] & 0x80) == 0) 165 | { 166 | ++i; 167 | break; 168 | } 169 | } 170 | totlen += i; 171 | 172 | if(i + 2 >= buffer.length) 173 | return null; 174 | 175 | topiclen = buffer[i++] << 8; 176 | topiclen |= buffer[i++]; 177 | 178 | if(i + topiclen > buffer.length) 179 | return null; 180 | 181 | // *length = topiclen; 182 | let topic = []; 183 | for(let j=0; j= blength) 209 | return null; 210 | 211 | topiclen = buffer[i++] << 8; 212 | topiclen |= buffer[i++]; 213 | 214 | if(i + topiclen >= blength) 215 | return null; 216 | 217 | i += topiclen; 218 | 219 | if(mqtt_get_qos(buffer) > 0) 220 | { 221 | if(i + 2 >= blength) 222 | return null; 223 | i += 2; 224 | } 225 | 226 | if(totlen < i) 227 | return null; 228 | 229 | if(totlen <= blength) 230 | length = totlen - i; 231 | else 232 | length = blength - i; 233 | 234 | let data = []; 235 | for(let j=0; j> 8); 279 | variable_header.push(msgId& 0xFF); 280 | 281 | let totalLen = variable_header.length; 282 | let fixed_header = mqtt_fixed_header(totalLen, mqtt_msg_type.MQTT_MSG_TYPE_PUBACK, 0, 0, 0); 283 | return fixed_header.concat(variable_header); 284 | } 285 | 286 | var mqtt_msg_pubrec = function(msgId) { 287 | let variable_header = []; 288 | variable_header.push((msgId& 0xFFFF) >> 8); 289 | variable_header.push(msgId& 0xFF); 290 | 291 | let totalLen = variable_header.length; 292 | let fixed_header = mqtt_fixed_header(totalLen, mqtt_msg_type.MQTT_MSG_TYPE_PUBREC, 0, 0, 0); 293 | return fixed_header.concat(variable_header); 294 | } 295 | 296 | var mqtt_msg_pubrel = function(msgId) { 297 | let variable_header = []; 298 | variable_header.push((msgId& 0xFFFF) >> 8); 299 | variable_header.push(msgId& 0xFF); 300 | 301 | let totalLen = variable_header.length; 302 | let fixed_header = mqtt_fixed_header(totalLen, mqtt_msg_type.MQTT_MSG_TYPE_PUBREL, 0, 0, 0); 303 | return fixed_header.concat(variable_header); 304 | } 305 | 306 | var mqtt_msg_pubcomp = function(msgId) { 307 | let variable_header = []; 308 | variable_header.push((msgId& 0xFFFF) >> 8); 309 | variable_header.push(msgId& 0xFF); 310 | 311 | let totalLen = variable_header.length; 312 | let fixed_header = mqtt_fixed_header(totalLen, mqtt_msg_type.MQTT_MSG_TYPE_PUBCOMP, 0, 0, 0); 313 | return fixed_header.concat(variable_header); 314 | } 315 | 316 | var mqtt_msg_subscribe = function(msgId, info) { 317 | if(!info) 318 | return null; 319 | let variable_header = []; 320 | let payload = []; 321 | 322 | variable_header.push_len(msgId); 323 | 324 | if(typeof info === 'string') { 325 | payload.push_str(info); 326 | payload.push(0); 327 | } else { 328 | var topic = Object.keys(info); 329 | topic.map(t => { 330 | payload.push_str(t); 331 | payload.push(info[t]); 332 | }); 333 | } 334 | 335 | 336 | let totalLen = variable_header.length + payload.length; 337 | let fixed_header = mqtt_fixed_header(totalLen, mqtt_msg_type.MQTT_MSG_TYPE_SUBSCRIBE, 0, 1, 0); 338 | return fixed_header.concat(variable_header, payload); 339 | } 340 | 341 | function mqtt_get_id(buffer) 342 | { 343 | var length = buffer.length; 344 | if(length < 1) 345 | return 0; 346 | 347 | switch(mqtt_get_type(buffer)) 348 | { 349 | case mqtt_msg_type.MQTT_MSG_TYPE_PUBLISH: 350 | { 351 | let i; 352 | let topiclen; 353 | 354 | for(i = 1; i < length; ++i) 355 | { 356 | if((buffer[i] & 0x80) == 0) 357 | { 358 | ++i; 359 | break; 360 | } 361 | } 362 | 363 | if(i + 2 >= length) 364 | return 0; 365 | topiclen = buffer[i++] << 8; 366 | topiclen |= buffer[i++]; 367 | 368 | if(i + topiclen >= length) 369 | return 0; 370 | i += topiclen; 371 | 372 | if(mqtt_get_qos(buffer) > 0) 373 | { 374 | if(i + 2 >= length) 375 | return 0; 376 | } 377 | else 378 | return 0; 379 | 380 | return (buffer[i] << 8) | buffer[i + 1]; 381 | } 382 | case mqtt_msg_type.MQTT_MSG_TYPE_PUBACK: 383 | case mqtt_msg_type.MQTT_MSG_TYPE_PUBREC: 384 | case mqtt_msg_type.MQTT_MSG_TYPE_PUBREL: 385 | case mqtt_msg_type.MQTT_MSG_TYPE_PUBCOMP: 386 | case mqtt_msg_type.MQTT_MSG_TYPE_SUBACK: 387 | case mqtt_msg_type.MQTT_MSG_TYPE_UNSUBACK: 388 | case mqtt_msg_type.MQTT_MSG_TYPE_SUBSCRIBE: 389 | { 390 | // This requires the remaining length to be encoded in 1 byte, 391 | // which it should be. 392 | if(length >= 4 && (buffer[1] & 0x80) == 0) 393 | return (buffer[2] << 8) | buffer[3]; 394 | else 395 | return 0; 396 | } 397 | default: 398 | return 0; 399 | } 400 | } 401 | var mqtt_get_total_length = function(buffer) 402 | { 403 | let i; 404 | let totlen = 0; 405 | let length = buffer.length; 406 | 407 | for(i = 1; i < length; ++i) 408 | { 409 | totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); 410 | if((buffer[i] & 0x80) == 0) 411 | { 412 | ++i; 413 | break; 414 | } 415 | } 416 | totlen += i; 417 | 418 | return totlen; 419 | } 420 | var mqtt_msg_unsubscribe = function() { 421 | 422 | } 423 | var mqtt_msg_pingreq = function() { 424 | return mqtt_fixed_header(0, mqtt_msg_type.MQTT_MSG_TYPE_PINGREQ, 0, 0, 0); 425 | } 426 | 427 | var mqtt_msg_pingresp = function() { 428 | 429 | } 430 | 431 | var mqtt_msg_disconnect = function() { 432 | 433 | } 434 | 435 | function mqtt_get_type(buffer) { return (buffer[0] & 0xf0) >> 4; } 436 | function mqtt_get_dup(buffer) { return (buffer[0] & 0x08) >> 3; } 437 | function mqtt_get_qos(buffer) { return (buffer[0] & 0x06) >> 1; } 438 | function mqtt_get_retain(buffer) { return (buffer[0] & 0x01); } 439 | 440 | var mqtt_msg_parse = function(data) { 441 | let msgType = null; 442 | for(let msgKey in mqtt_msg_type) { 443 | if(mqtt_msg_type[msgKey] == mqtt_get_type(data)) { 444 | msgType = mqtt_msg_type[msgKey]; 445 | } 446 | } 447 | console.log(msgType); 448 | } 449 | export { 450 | mqtt_msg_type, 451 | mqtt_msg_connect, 452 | mqtt_msg_publish, 453 | mqtt_msg_puback, 454 | mqtt_msg_pubrec, 455 | mqtt_msg_pubrel, 456 | mqtt_msg_pubcomp, 457 | mqtt_msg_subscribe, 458 | mqtt_msg_unsubscribe, 459 | mqtt_msg_pingreq, 460 | mqtt_msg_pingresp, 461 | mqtt_msg_parse, 462 | mqtt_get_type, 463 | mqtt_get_dup, 464 | mqtt_get_qos, 465 | mqtt_get_retain, 466 | mqtt_get_id, 467 | mqtt_get_total_length, 468 | mqtt_get_publish_topic, 469 | mqtt_get_publish_data, 470 | } 471 | -------------------------------------------------------------------------------- /mqtt-store.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: TuanPM 3 | * @Date: 2016-05-24 20:34:09 4 | * @Last Modified by: TuanPM 5 | * @Last Modified time: 2016-05-25 15:00:22 6 | */ 7 | 8 | 'use strict'; 9 | 10 | import EventEmitter from 'EventEmitter'; 11 | 12 | class Store extends EventEmitter { 13 | constructor() { 14 | super(); 15 | this._inflights = {}; 16 | this._len = 0; 17 | this._msgIdx = []; 18 | } 19 | 20 | put(packet, cb) { 21 | this._inflights[packet.msgId] = packet; 22 | this._len ++; 23 | this._msgIdx.push(packet.msgId); 24 | 25 | cb && cb(); 26 | this.emit('available', packet); 27 | return this; 28 | } 29 | 30 | 31 | close(cb) { 32 | this._inflights = null; 33 | if (cb) { 34 | cb(); 35 | } 36 | this.emit('closed'); 37 | } 38 | 39 | get(packet, cb) { 40 | packet = this._inflights[packet.msgId]; 41 | if (packet) { 42 | cb && cb(null, packet); 43 | } else if (cb) { 44 | cb(new Error('missing packet')); 45 | } 46 | return packet; 47 | } 48 | 49 | getOldest() { 50 | var packet = null; 51 | if(this.available()) { 52 | var packetIdx = this._msgIdx[0]; 53 | packet = this._inflights[packetIdx]; 54 | } 55 | return packet; 56 | } 57 | 58 | del(packet, cb) { 59 | packet = this._inflights[packet.msgId]; 60 | if (packet) { 61 | delete this._inflights[packet.msgId]; 62 | this._len --; 63 | var idx = this._msgIdx.indexOf(packet.msgId); 64 | if(idx >= 0) { 65 | this._msgIdx.splice(idx, 1); 66 | } 67 | cb && cb(null, packet); 68 | } else if (cb) { 69 | cb(new Error('missing packet')); 70 | } 71 | return this; 72 | } 73 | 74 | available() { 75 | return this._len > 0; 76 | } 77 | 78 | check() { 79 | if(this.available()) 80 | this.emit('available'); 81 | } 82 | } 83 | 84 | 85 | export default Store; 86 | -------------------------------------------------------------------------------- /mqtt.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: TuanPM 3 | * @Date: 2016-05-24 14:19:28 4 | * @Last Modified by: Tuan PM 5 | * @Last Modified time: 2016-05-26 12:10:39 6 | */ 7 | 8 | 'use strict'; 9 | 10 | import EventEmitter from 'EventEmitter'; 11 | import Store from './mqtt-store'; 12 | import { 13 | mqtt_msg_type, 14 | mqtt_msg_connect, 15 | mqtt_msg_publish, 16 | mqtt_msg_puback, 17 | mqtt_msg_pubrec, 18 | mqtt_msg_pubrel, 19 | mqtt_msg_pubcomp, 20 | mqtt_msg_subscribe, 21 | mqtt_msg_unsubscribe, 22 | mqtt_msg_pingreq, 23 | mqtt_msg_pingresp, 24 | mqtt_msg_parse, 25 | mqtt_get_type, 26 | mqtt_get_dup, 27 | mqtt_get_qos, 28 | mqtt_get_retain, 29 | mqtt_get_id, 30 | mqtt_get_publish_topic, 31 | mqtt_get_publish_data, 32 | mqtt_get_total_length, 33 | } from './mqtt-msg'; 34 | 35 | const defaultConnectOptions = { 36 | keepalive: 120, 37 | protocolId: 'MQTT', 38 | reconnectPeriod: 1000, 39 | connectTimeout: 30 * 1000, 40 | clean: true 41 | }; 42 | 43 | class MqttClient extends EventEmitter { 44 | 45 | constructor(options) { 46 | super(); 47 | var self = this; 48 | this.ws = null; 49 | this.options = options || {}; 50 | if(!this.options.keepalive) 51 | this.options.keepalive = 120; 52 | this.outgoingStore = this.options.outgoingStore || new Store(); 53 | this.incomingStore = this.options.incomingStore || new Store(); 54 | 55 | this.queueQoSZero = null == this.options.queueQoSZero ? true : this.options.queueQoSZero; 56 | 57 | // Ping timer, setup in _setupPingTimer 58 | this.pingTimer = null; 59 | // Is the client connected? 60 | this.connected = false; 61 | // Are we disconnecting? 62 | this.disconnecting = false; 63 | // Packet queue 64 | this.queue = []; 65 | // connack timer 66 | this.connackTimer = null; 67 | // Reconnect timer 68 | this.reconnectTimer = null; 69 | // MessageIDs starting with 1 70 | this.nextId = Math.floor(Math.random() * 65535); 71 | 72 | // Inflight callbacks 73 | this.outgoing = null; 74 | 75 | //Websocket connected 76 | this.addListener('connected', () => { 77 | //send and store pendding connect packet 78 | self._sendPacket(mqtt_msg_connect(self.options)); 79 | }); 80 | 81 | //MQTT connected 82 | this.addListener('connect', () => { 83 | self.connected = true; 84 | //setup ping timer 85 | // self.subscribe({'/topic': 0}, function(data) { 86 | // console.log('subscribe success data', data); 87 | // }) 88 | console.log('mqtt connected'); 89 | // self.outgoingStore.addListener('insert', function(data) { 90 | 91 | // }); 92 | self.outgoingStore.check(); 93 | 94 | self.pingTimer = setInterval(() => { 95 | //self.sendping(); 96 | self._sendPacket(mqtt_msg_pingreq()); 97 | console.log('ping'); 98 | }, this.options.keepalive*1000/2); 99 | }); 100 | 101 | this.addListener('close', () => { 102 | self.connected = false; 103 | clearInterval(this.pingTimer); 104 | }); 105 | 106 | this.addListener('data', (data) => { 107 | self._handlePacket(data); 108 | }); 109 | 110 | 111 | this.outgoingStore.addListener('available', function(data) { 112 | /* if data availale */ 113 | if(self.connected && self._isOutgoingEmpty()) { 114 | 115 | var msg = self.outgoingStore.getOldest(); 116 | self._sendPacket(msg.data); 117 | 118 | if(mqtt_get_type(msg.data) == mqtt_msg_type.MQTT_MSG_TYPE_PUBLISH 119 | && mqtt_get_qos(msg.data) == 0) { 120 | 121 | console.log('publish with qos = 0, msgId = ' + msg.msgId); 122 | if(msg.cb) 123 | msg.cb(); 124 | self.outgoingStore.del(msg); 125 | self._clearOutgoing(); 126 | } 127 | } 128 | }); 129 | } 130 | 131 | 132 | _clearOutgoing() { 133 | this.outgoing = null; 134 | } 135 | 136 | _isOutgoingEmpty() { 137 | return this.outgoing == null; 138 | } 139 | 140 | _handlePacket(data) { 141 | if(!data) return; 142 | 143 | var dataBuffer = new Uint8Array(data); 144 | 145 | 146 | var self = this; 147 | 148 | var msg_type = mqtt_get_type(dataBuffer); 149 | var msg_qos = mqtt_get_qos(dataBuffer); 150 | var msg_id = mqtt_get_id(dataBuffer); 151 | var total_len= mqtt_get_total_length(dataBuffer); 152 | 153 | switch(msg_type) { 154 | case mqtt_msg_type.MQTT_MSG_TYPE_CONNACK: 155 | if(dataBuffer[3] != 0 || mqtt_get_type(self.outgoing) != mqtt_msg_type.MQTT_MSG_TYPE_CONNECT) 156 | return self.emit('error', {type: 'connect', msgId: dataBuffer[3]}); 157 | 158 | self._clearOutgoing(); 159 | self.emit('connect'); 160 | break; 161 | 162 | case mqtt_msg_type.MQTT_MSG_TYPE_PINGRESP: 163 | console.log('MQTT_MSG_TYPE_PINGRESP'); 164 | self._clearOutgoing(); 165 | break; 166 | 167 | case mqtt_msg_type.MQTT_MSG_TYPE_SUBACK: 168 | console.log('MQTT_MSG_TYPE_SUBACK'); 169 | var msg = self.outgoingStore.get({msgId: msg_id}); 170 | if(msg) { 171 | self.outgoingStore.del({msgId: msg_id}); 172 | self._clearOutgoing(); 173 | if(msg.cb) 174 | msg.cb(); 175 | } 176 | break; 177 | 178 | case mqtt_msg_type.MQTT_MSG_TYPE_PUBLISH: 179 | console.log('MQTT_MSG_TYPE_PUBLISH'); 180 | if(msg_qos == 1) { 181 | self._sendPacket(mqtt_msg_puback(msg_id)); 182 | console.log('qos1'); 183 | } else if(msg_qos == 2) { 184 | self._sendPacket(mqtt_msg_pubrec(msg_id)); 185 | console.log('qos2'); 186 | 187 | } 188 | 189 | self._deliverPublish(dataBuffer); 190 | break; 191 | case mqtt_msg_type.MQTT_MSG_TYPE_PUBACK: 192 | console.log('MQTT_MSG_TYPE_PUBACK'); 193 | 194 | var msg = self.outgoingStore.get({msgId: msg_id}); 195 | if(msg.msgId == msg_id && mqtt_get_type(msg.data) == mqtt_msg_type.MQTT_MSG_TYPE_PUBLISH) { 196 | if(msg.cb) 197 | msg.cb(); 198 | self.outgoingStore.del({msgId: msg_id}); 199 | self._clearOutgoing(); 200 | 201 | } 202 | break; 203 | case mqtt_msg_type.MQTT_MSG_TYPE_PUBREC: 204 | console.log('MQTT_MSG_TYPE_PUBREC'); 205 | if(msg_id == mqtt_get_id(self.outgoing)) { 206 | self._sendPacket(mqtt_msg_pubrel(msg_id)); 207 | } 208 | break; 209 | case mqtt_msg_type.MQTT_MSG_TYPE_PUBREL: 210 | console.log('MQTT_MSG_TYPE_PUBREL'); 211 | if(msg_id == mqtt_get_id(self.outgoing)) { 212 | self._sendPacket(mqtt_msg_pubcomp(msg_id)); 213 | } 214 | break; 215 | case mqtt_msg_type.MQTT_MSG_TYPE_PUBCOMP: 216 | console.log('MQTT_MSG_TYPE_PUBCOMP'); 217 | var msg = self.outgoingStore.get({msgId: msg_id}); 218 | if(msg.msgId == msg_id && mqtt_get_type(msg.data) == mqtt_msg_type.MQTT_MSG_TYPE_PUBLISH) { 219 | if(msg.cb) 220 | msg.cb(); 221 | self.outgoingStore.del({msgId: msg_id}); 222 | self._clearOutgoing(); 223 | 224 | } 225 | 226 | } 227 | 228 | self.outgoingStore.check(); 229 | //switch packet data 230 | //if pendding packet == connect && msgId == pendding.msgId then emit event 'connect' 231 | } 232 | 233 | _deliverPublish(buffer) { 234 | let topic = mqtt_get_publish_topic(buffer); 235 | let data = mqtt_get_publish_data(buffer); 236 | //console.log(topic + ':' + data); 237 | this.emit('message', topic, data); 238 | } 239 | 240 | _nextId () { 241 | var id = this.nextId++; 242 | // Ensure 16 bit unsigned int: 243 | if (65535 === id) { 244 | this.nextId = 1; 245 | } 246 | return id; 247 | } 248 | 249 | _sendPacket(packet) { 250 | if(this.ws) { 251 | this.outgoing = packet; 252 | this.ws.send(new Uint8Array(packet)); 253 | } 254 | } 255 | 256 | connect(uri) { 257 | var self = this; 258 | 259 | self.ws = new WebSocket(uri); 260 | 261 | self.ws.onopen = () => { 262 | // connection opened 263 | console.log('connected'); 264 | self.emit('connected'); 265 | 266 | }; 267 | 268 | self.ws.onmessage = (e) => { 269 | // a message was received 270 | self.emit('data', e.data); 271 | }; 272 | 273 | self.ws.onerror = (e) => { 274 | // an error occurred 275 | self.emit('error', e.message); 276 | 277 | }; 278 | 279 | self.ws.onclose = (e) => { 280 | // connection closed 281 | self.emit('close', e); 282 | //console.log(e.code, e.reason); 283 | 284 | }; 285 | } 286 | 287 | publish(topic, message, options, cb) { 288 | var msgId = this._nextId(); 289 | this.outgoingStore.put({msgId: msgId, cb: cb, data: mqtt_msg_publish(msgId, topic, message, options)}); 290 | } 291 | 292 | subscribe(data, cb) { 293 | var msgId = this._nextId(); 294 | this.outgoingStore.put({msgId: msgId, cb: cb, data: mqtt_msg_subscribe(msgId, data)}); 295 | } 296 | 297 | unsubscribe() { 298 | 299 | } 300 | 301 | } 302 | 303 | export { 304 | MqttClient 305 | } 306 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-mqttjs", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "mqtt.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/tuanpmt/react-native-mqttjs.git" 12 | }, 13 | "author": "Tuan PM (http://tuanpm.net)", 14 | "license": "ISC", 15 | "bugs": { 16 | "url": "https://github.com/tuanpmt/react-native-mqttjs/issues" 17 | }, 18 | "homepage": "https://github.com/tuanpmt/react-native-mqttjs#readme", 19 | "dependencies": { 20 | 21 | } 22 | } 23 | --------------------------------------------------------------------------------