├── .npm └── package │ ├── .gitignore │ ├── README │ └── npm-shrinkwrap.json ├── .versions ├── README.md ├── lib ├── mqtt.js └── mqtt_collection.js ├── package.js └── smart.json /.npm/package/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.npm/package/README: -------------------------------------------------------------------------------- 1 | This directory and the files immediately inside it are automatically generated 2 | when you change this package's NPM dependencies. Commit the files in this 3 | directory (npm-shrinkwrap.json, .gitignore, and this README) to source control 4 | so that others run the same versions of sub-dependencies. 5 | 6 | You should NOT check in the node_modules directory that Meteor automatically 7 | creates; if you are using git, the .gitignore file tells git to ignore it. 8 | -------------------------------------------------------------------------------- /.npm/package/npm-shrinkwrap.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "mqtt": { 4 | "version": "1.2.0", 5 | "dependencies": { 6 | "commist": { 7 | "version": "1.0.0", 8 | "dependencies": { 9 | "leven": { 10 | "version": "1.0.2" 11 | } 12 | } 13 | }, 14 | "concat-stream": { 15 | "version": "1.4.8", 16 | "dependencies": { 17 | "typedarray": { 18 | "version": "0.0.6" 19 | }, 20 | "readable-stream": { 21 | "version": "1.1.13", 22 | "dependencies": { 23 | "core-util-is": { 24 | "version": "1.0.1" 25 | }, 26 | "isarray": { 27 | "version": "0.0.1" 28 | }, 29 | "string_decoder": { 30 | "version": "0.10.31" 31 | } 32 | } 33 | } 34 | } 35 | }, 36 | "end-of-stream": { 37 | "version": "1.1.0", 38 | "dependencies": { 39 | "once": { 40 | "version": "1.3.2", 41 | "dependencies": { 42 | "wrappy": { 43 | "version": "1.0.1" 44 | } 45 | } 46 | } 47 | } 48 | }, 49 | "help-me": { 50 | "version": "0.1.0", 51 | "dependencies": { 52 | "pump": { 53 | "version": "1.0.0", 54 | "dependencies": { 55 | "once": { 56 | "version": "1.3.2", 57 | "dependencies": { 58 | "wrappy": { 59 | "version": "1.0.1" 60 | } 61 | } 62 | } 63 | } 64 | } 65 | } 66 | }, 67 | "inherits": { 68 | "version": "2.0.1" 69 | }, 70 | "minimist": { 71 | "version": "1.1.1" 72 | }, 73 | "mqtt-connection": { 74 | "version": "2.1.1", 75 | "dependencies": { 76 | "reduplexer": { 77 | "version": "1.1.0" 78 | }, 79 | "through2": { 80 | "version": "0.6.5" 81 | } 82 | } 83 | }, 84 | "mqtt-packet": { 85 | "version": "3.2.0", 86 | "dependencies": { 87 | "bl": { 88 | "version": "0.9.4" 89 | } 90 | } 91 | }, 92 | "readable-stream": { 93 | "version": "1.0.33", 94 | "dependencies": { 95 | "core-util-is": { 96 | "version": "1.0.1" 97 | }, 98 | "isarray": { 99 | "version": "0.0.1" 100 | }, 101 | "string_decoder": { 102 | "version": "0.10.31" 103 | } 104 | } 105 | }, 106 | "websocket-stream": { 107 | "version": "1.5.0", 108 | "dependencies": { 109 | "duplexify": { 110 | "version": "3.4.0", 111 | "dependencies": { 112 | "end-of-stream": { 113 | "version": "1.0.0", 114 | "dependencies": { 115 | "once": { 116 | "version": "1.3.2", 117 | "dependencies": { 118 | "wrappy": { 119 | "version": "1.0.1" 120 | } 121 | } 122 | } 123 | } 124 | }, 125 | "readable-stream": { 126 | "version": "1.1.13", 127 | "dependencies": { 128 | "core-util-is": { 129 | "version": "1.0.1" 130 | }, 131 | "isarray": { 132 | "version": "0.0.1" 133 | }, 134 | "string_decoder": { 135 | "version": "0.10.31" 136 | } 137 | } 138 | } 139 | } 140 | }, 141 | "through2": { 142 | "version": "0.6.5" 143 | }, 144 | "ws": { 145 | "version": "0.7.2", 146 | "dependencies": { 147 | "options": { 148 | "version": "0.0.6" 149 | }, 150 | "ultron": { 151 | "version": "1.0.1" 152 | }, 153 | "bufferutil": { 154 | "version": "1.1.0", 155 | "dependencies": { 156 | "bindings": { 157 | "version": "1.2.1" 158 | }, 159 | "nan": { 160 | "version": "1.8.4" 161 | } 162 | } 163 | }, 164 | "utf-8-validate": { 165 | "version": "1.1.0", 166 | "dependencies": { 167 | "bindings": { 168 | "version": "1.2.1" 169 | }, 170 | "nan": { 171 | "version": "1.8.4" 172 | } 173 | } 174 | } 175 | } 176 | } 177 | } 178 | }, 179 | "xtend": { 180 | "version": "4.0.0" 181 | } 182 | } 183 | } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /.versions: -------------------------------------------------------------------------------- 1 | meteor@1.1.6 2 | perak:mqtt-collection@1.0.3 3 | underscore@1.0.3 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | mqtt-collection package for Meteor 2 | ================================== 3 | 4 | - Messages received via MQTT broker are written into collection 5 | 6 | - Data inserted into collection is broadcasted via MQTT 7 | 8 | 9 | Example 10 | ------- 11 | 12 | ### Connect to MQTT broker and subscribe to topic (server side only) 13 | 14 | ``` 15 | MyCollection.mqttConnect("mqtt://test.mosquitto.org", ["presence"], {}, {}); 16 | ``` 17 | 18 | We are now connected and subscribed to "presence" topic. Anything published to "presence" MQTT topic will be written into MyCollection. 19 | 20 | 21 | ### Broadcast data (works both on client and server) 22 | 23 | ``` 24 | MyCollection.insert({ topic: "presence", message: "Hello world! :)", broadcast: true }); 25 | ``` 26 | 27 | You need to insert three **mandatory** fields: `topic`, `message` and `broadcast` and your message will be broadcasted via MQTT broker to specified topic. 28 | 29 | 30 | Functions 31 | ========= 32 | 33 | Collection.mqttConnect(uri, topics, options, mqttOptions) 34 | -------------------------------------------- 35 | 36 | Establishes connection to MQTT broker and subscribes to listed topic(s). 37 | 38 | **Arguments:** 39 | 40 | - `uri` is mqtt broker address 41 | - `topics` is array of strings or single string - topic name(s) to subscribe on connect 42 | - `options` is object with following properties: 43 | ``` 44 | { 45 | insert: false, 46 | raw: false 47 | } 48 | ``` 49 | - `insert` - if set to true, each message will be inserted into collection (and your collection will grow!). If this option is not set (or set to false) messages will be upsert-ed (you'l have single document for each topic). Default: false 50 | - `raw` - if set to true, received string will be written as-is. If this option is not set (or set to false) received string will be converted to object with `JSON.parse()`. Default: false 51 | - `mqttOptions` is an object that is supplied to `mqtt.connect([url],options)` in the MQTT.js library for configuring the underlying options of the MQTT.js-client. See the docs. 52 | 53 | Collection.mqttDisconnect() 54 | --------------------------- 55 | 56 | Closes connection to MQTT broker 57 | 58 | 59 | Collection.mqttSubscribe(topics) 60 | -------------------------------- 61 | 62 | Subscribe to specified topic(s). Works only **after** MQTT connection is established. 63 | 64 | **Arguments:** 65 | 66 | - `topics` is array of strings or single string - topic name(s) to subscribe 67 | 68 | 69 | Live example 70 | ============ 71 | 72 | You can find example application using this package here. 73 | 74 | 75 | That's all folks :) 76 | -------------------------------------------------------------------------------- /lib/mqtt.js: -------------------------------------------------------------------------------- 1 | mqtt = Npm.require('mqtt'); -------------------------------------------------------------------------------- /lib/mqtt_collection.js: -------------------------------------------------------------------------------- 1 | var Fiber = Npm.require("fibers"); 2 | 3 | var Mongo = Package.mongo.Mongo; 4 | 5 | Mongo.Collection.prototype.mqttConnect = function(uri, topics, options, mqttOptions) { 6 | var self = this; 7 | this.mqttDisconnect(); 8 | 9 | this.options = options || {}; 10 | this.mqttOptions = mqttOptions || {}; 11 | 12 | this._mqttClient = mqtt.connect(uri,self.mqttOptions); 13 | 14 | this._mqttClient.on("connect", function() { 15 | self.mqttSubscribe(topics); 16 | }); 17 | 18 | this._mqttClient.on("message", function(topic, message) { 19 | var msg = message.toString(); 20 | if(!self.options.raw) { 21 | try { 22 | msg = JSON.parse(msg); 23 | } catch(e) { 24 | } 25 | } 26 | 27 | Fiber(function() { 28 | 29 | if(self.options.insert) { 30 | self.insert({ 31 | topic: topic, 32 | message: msg 33 | }, function(e, r) { 34 | if(e) { 35 | console.log(e); 36 | } else { 37 | if(self.options.insertLimit) { 38 | var insertLimit = parseInt(self.options.insertLimit); 39 | if(!isNaN(insertLimit)) { 40 | while(self.find({ topic: topic }).count() > insertLimit) { 41 | var removeId = self.findOne({ topic: topic }, { sort: [["createdAt", "asc"]] }); 42 | if(removeId) { 43 | self.remove({ _id: removeId._id }); 44 | } 45 | } 46 | } 47 | } 48 | } 49 | }); 50 | } else { 51 | self.upsert( 52 | { 53 | topic: topic 54 | }, 55 | { 56 | $set: { 57 | topic: topic, 58 | message: msg 59 | } 60 | }, 61 | { 62 | }, 63 | function(e, r) { 64 | if(e) console.log(e); 65 | }); 66 | } 67 | }).run(); 68 | }); 69 | 70 | var init = true; 71 | this.find().observeChanges({ 72 | added: function(id, doc) { 73 | if(!init) { 74 | if(doc && doc.topic && doc.message && doc.broadcast && self._mqttClient) { 75 | var msg = typeof doc.message === 'object' ? JSON.stringify(doc.message) : doc.message + ""; 76 | self.remove({ _id: id }); 77 | self._mqttClient.publish(doc.topic, msg); 78 | } 79 | } 80 | } 81 | }); 82 | init = false; 83 | }; 84 | 85 | Mongo.Collection.prototype.mqttDisconnect = function() { 86 | if(this._mqttClient) this._mqttClient.end(); 87 | this._mqttClient = null; 88 | }; 89 | 90 | Mongo.Collection.prototype.mqttSubscribe = function(topics) { 91 | var self = this; 92 | if(!this._mqttClient) return; 93 | if(!topics) return; 94 | 95 | if(typeof topics == "string" || topics instanceof String) { 96 | this._mqttClient.subscribe(topics); 97 | } else if(_.isArray(topics)) { 98 | _.each(topics, function(topic) { 99 | self._mqttClient.subscribe(topic); 100 | }); 101 | } 102 | }; -------------------------------------------------------------------------------- /package.js: -------------------------------------------------------------------------------- 1 | Package.describe({ 2 | name: "perak:mqtt-collection", 3 | version: "1.0.5", 4 | summary: "IoT for Meteor - send/receive MQTT messages via collections", 5 | git: "https://github.com/perak/meteor-mqtt-collection.git", 6 | documentation: "README.md" 7 | }); 8 | 9 | Npm.depends({ 10 | "mqtt": "1.13.0" 11 | }); 12 | 13 | Package.onUse(function(api) { 14 | api.versionsFrom("1.0"); 15 | api.addFiles("lib/mqtt.js", "server"); 16 | api.addFiles("lib/mqtt_collection.js", "server"); 17 | api.export("mqtt"); 18 | }); 19 | -------------------------------------------------------------------------------- /smart.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mqtt", 3 | "description": "IoT - send/receive MQTT messages via collections", 4 | "homepage": "https://github.com/perak/meteor-mqtt-collection", 5 | "author": "Petar Korponaic", 6 | "version": "1.0.5", 7 | "git": "https://github.com/perak/meteor-mqtt-collection.git", 8 | "packages": {} 9 | } 10 | --------------------------------------------------------------------------------