UNPKG

irrelon-reactor-device

Version:

A module for easily creating reactor core devices.

263 lines (222 loc) 7.42 kB
import ForerunnerDB from 'forerunnerdb'; import AN from 'irrelon-reactor-autonet'; class Device extends AN { constructor (deviceData) { super(deviceData._id); var self = this; self._data = deviceData; self.fdb = new ForerunnerDB(); self.db = self.fdb.db(deviceData._id); // Set the persist plugin's data folder (where to store data files) self.db.persist.dataDir('./data'); // Tell the database to load and save data for collections automatically // this will auto-persist any data inserted in the database to disk // and automatically load it when the server is restarted self.db.persist.auto(true); // Get the state collection self._state = self.db.collection('state'); self._state.once('load', function () { self._ready = true; setTimeout(function () { self.emit('ready'); },100); }); } /** * Starts a device service on the specified host and port. This method will * wait for the ready event to be fired before it will start the service. * @param {String} host The IP address to start the device service on. Can be * set to 0.0.0.0 to listen on all available IPs. * @param {Number} port The port to listen on. If set to zero then the port * will be randomly selected from any unused ports on the host. * @param {Function} callback Callback function will be called with * result of device start. */ startDevice (host, port, callback) { var self = this; if (self._ready) { self.start(host, port, callback); } else { self.once('ready', function () { self.start(host, port, callback); }); } } /** * Starts a server on the specified host and port. * @param {String} host The IP address to start the server on. Can be set to * 0.0.0.0 to listen on all available IPs. * @param {Number} port The port to listen on. If set to zero then the port * will be randomly selected from any unused ports on the host. * @param {Function} callback Callback function will be called with * result of server start. */ start (host, port, callback) { var self = this; // Define default device routes self.getRoute('/device/:deviceId/state', function (req, res) { var params = req.params; if (params && params.deviceId) { if (params.deviceId === self._data._id) { res.status(200).send(self._state.find({ deviceId: params.deviceId })); } else { res.status(500).send('Device not found with id: ' + params.deviceId); } } }); self.getRoute('/device/:deviceId/state/:stateId', function (req, res) { var params = req.params; if (params && params.deviceId) { if (params.deviceId === self._data._id) { res.status(200).send(self._state.find({ _id: params.stateId, deviceId: params.deviceId })); } else { res.status(500).send('Device not found with id: ' + params.deviceId); } } }); self.putRoute('/device/:deviceId/state/:stateId', function (req, res) { var params = req.params, body = req.body, client; if (params && params.deviceId && params.stateId) { // Check if the device is this one or a third party if (params.deviceId === self._data._id) { // The device is this one, request a state value change self.updateRequest(params.stateId, body.val, function (err, finalVal) { if (!err) { res.status(200).send(self._state.find({ _id: params.stateId })); } else { res.status(500).send(err); } }); } else { // The update was not for this device, find the client that sent it // in our clients list. client = self.client.findById(params.deviceId); if (client) { self.emit('clientStateUpdate', client.data, params.stateId, body.oldVal, body.val); } // Tell the sender we got the message res.sendStatus(200); } } }); // Start server via AutoNet module super.start(host, port, callback); } /** * Sends an advert packet over the current network for other zero-config * services to pick up. One packet is sent for every advertise call. */ advertise () { super.advertise(this._data); } /** * Gets a state from the device and calls the callback function * with the data for the state. * @param {String} stateId The ID of the state to retrieve. * @param {Function} callback The callback function to pass state * data back to (err, data). * @returns {*} */ state (stateId, callback) { var data = this._state.findById(stateId); if (callback) { callback(false, data); } return data; } /** * Updates the specified state with the specified value. * @param {String} stateId The ID of the state to update. * @param {*} val The new value to assign to the state. * @param {Function} callback Callback function (err, data). */ update (stateId, val, callback) { var newData, update, oldData = this._state.findById(stateId) || { _id: stateId, val: undefined }; update = this._state.upsert({ _id: stateId, val: val, deviceId: this._data._id }); newData = this._state.findById(stateId); if (oldData.val !== newData.val) { // Tell all clients about this state update this.broadcast('PUT', '/device/' + this._data._id + '/state/' + stateId, { val: newData.val, oldVal: oldData.val }, function () { }); this.emit('stateUpdate', stateId, oldData.val, newData.val); } if (callback) { callback(false, newData); } } /** * Makes a request to update the specified state to the specified value. * @param {String} stateId The ID of the state to modify. * @param {*} newVal The new value to store against the state. * @param {Function} callback The callback function to call when the update * request has been processed (err, data). */ updateRequest (stateId, newVal, callback) { var self = this, handled = false, finishFunc, oldData = this._state.findById(stateId) || { _id: stateId, val: undefined }; finishFunc = function (err, finalVal) { if (handled === false) { handled = true; if (!err) { // Update the state self.update(stateId, finalVal, callback); } else { if (callback) { callback(err); } } } }; if (this.willEmit('stateUpdateRequest')) { this.emit('stateUpdateRequest', stateId, oldData.val, newVal, finishFunc); } else { // No listener defined for event 'stateUpdateRequest', just callback success finishFunc(false, newVal); } // If the handler has not been executed within 30 seconds, call the callback wit // a timeout error. The programmer has either not handled the stateUpdateRequest // event correctly or their code is taking too long to respond. setTimeout(function () { if (handled === false) { // Callback with an error handled = true; if (callback) { callback('ERR: IRRELON_REACTOR_001 - The service took too long to respond. This is usually caused by the reactor device not correctly hooking the stateUpdateRequest event or not calling the callback in that event.'); } } }, 30000); } delete (stateId, callback) { var data = this._state.findById(stateId), err = false; if (data) { this._state.removeById(stateId); } else { err = 'No matching record found'; } if (callback) { callback(err, data); } } } export default Device;