UNPKG

irrelon-reactor-device

Version:

A module for easily creating reactor core devices.

318 lines (258 loc) 10.5 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; var _forerunnerdb = require('forerunnerdb'); var _forerunnerdb2 = _interopRequireDefault(_forerunnerdb); var _irrelonReactorAutonet = require('irrelon-reactor-autonet'); var _irrelonReactorAutonet2 = _interopRequireDefault(_irrelonReactorAutonet); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var Device = function (_AN) { _inherits(Device, _AN); function Device(deviceData) { _classCallCheck(this, Device); var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(Device).call(this, deviceData._id)); var self = _this; self._data = deviceData; self.fdb = new _forerunnerdb2.default(); 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); }); return _this; } /** * 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. */ _createClass(Device, [{ key: 'startDevice', value: function 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. */ }, { key: 'start', value: function 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 _get(Object.getPrototypeOf(Device.prototype), 'start', this).call(this, 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. */ }, { key: 'advertise', value: function advertise() { _get(Object.getPrototypeOf(Device.prototype), 'advertise', this).call(this, 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 {*} */ }, { key: 'state', value: function 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). */ }, { key: 'update', value: function 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). */ }, { key: 'updateRequest', value: function updateRequest(stateId, newVal, callback) { var self = this, handled = false, finishFunc, oldData = this._state.findById(stateId) || { _id: stateId, val: undefined }; finishFunc = function finishFunc(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); } }, { key: 'delete', value: function _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); } } }]); return Device; }(_irrelonReactorAutonet2.default); exports.default = Device;