/**
* @project bcslib
* @author Brent Rahn <brent.rahn@gmail.com>
*/
/*jshint -W065 */
/**
* @namespace
*/
var BCS = {
version: '0.2.0'
};
BCS.Helpers = (function () {
/**
* Creates a `BCS.Helpers` Object
*
* Helper methods for accessing common values from the BCS.
* Added to BCS.Device by it's constructor
* @constructs BCS.Helpers
* @param {BCS.Device} device
*/
var Helpers = function (device) {
this.device = device;
};
/**
* Get Temperature Probe objects
* @returns {Promise.Object[]} Promise of a list of temperature probes
*/
Helpers.prototype.getProbes = function () {
var promises = [];
for(var i = 0; i < this.device.probeCount; i++) {
promises.push(this.device.read('temp/' + i));
}
return Promise.all(promises);
};
/**
* Get current temperature values
* @returns {Promise.number[]} Promise of a list of temperatures
*/
Helpers.prototype.getTempValues = function () {
return this.device.read('temp').then(function (response) {
return Promise.all(response.map(function (temp) { return temp / 10.0; }));
});
};
/**
* Get Discrete Input objects
* @returns {Promise.Object[]} Promise of a list of discrete inputs
*/
Helpers.prototype.getDins = function () {
var promises = [];
for(var i = 0; i < this.device.inputCount; i++) {
promises.push(this.device.read('din/' + i));
}
return Promise.all(promises);
};
/**
* Get current input values
* @returns {Promise.number[]} Promise of a list of on/off values for Dins
*/
Helpers.prototype.getDinValues = function () {
return this.device.read('din');
};
/**
* Get Outputs objects
* @returns {Promise.Object[]} Promise of a list of outputs
*/
Helpers.prototype.getOutputs = function () {
var promises = [];
for(var i = 0; i < this.device.outputCount; i++) {
promises.push(this.device.read('output/' + i));
}
return Promise.all(promises);
};
/**
* Get current output values
* @returns {Promise.number[]} Promise of a list of on/off values for outputs
*/
Helpers.prototype.getOutputValues = function () {
return this.device.read('output');
};
/**
* Get current timer values
* @param {Number} Process number
* @returns {Promise.Array.<BCS.Time>} Promise of a list of `BCS.Time` objects
*/
Helpers.prototype.getTimerValues = function (process) {
return this.device.read('process/' + process + '/timer')
.then(function (response) {
return Promise.all(response.map(function (timer) { return new BCS.Time(timer.value); }));
});
};
/**
* Get current timer values as strings
* @param {Number} Process number
* @returns {Promise.string[]} Promise of a list of times as strings
*/
Helpers.prototype.getTimerStrings = function (process) {
return this.getTimerValues(process)
.then(function (timers) {
return Promise.all(timers.map(function (timer) { return timer.toString(); }));
});
};
/**
* Get running processes
* @returns {Promise.Object[]} Promise of a list of running processes with some runtime data
*/
Helpers.prototype.getRunningProcesses = function () {
return this.device.read('poll')
.then(function (poll) {
return Promise.all(poll.process.map(function (p, i) {
p.id = i;
return p;
})
.filter(function (p) {return p.running;}));
});
};
/**
* Get Processes objects
* @returns {Promise.Object[]} Promise of a list of processes
*/
Helpers.prototype.getProcesses = function () {
var promises = [];
for(var i = 0; i < 8; i++) {
promises.push(this.device.read('process/' + i));
}
return Promise.all(promises);
};
return Helpers;
}());
BCS.Device = (function () {
/**
* Handles communication with BCS.
* @constructs BCS.Device
* @param {string} address IP Address for BCS
* @param {options} options for BCS. Currently only supports authentication. ex. `{auth: {username: 'x', password: 'y'}}`
* @property ready {boolean} Ready to communicate with BCS (other properties are not valid until ready is true)
* @property type {string} BCS type, eg `BCS-460`
* @property version {string} BCS firmware version, eg, `4.0.0`
* @property helpers {BCS.Helpers} Helpers object
* @property probeCount {number} The number of temp probes supported by the BCS hardware
* @property inputCount {number} The number of discrete inputs supported by the BCS hardware
* @property outputCount {number} The number of outputs supported by the BCS hardware
*/
var Device = function (address, options) {
var obj = this;
var parsedAuth;
this.address = address;
this.ready = false;
this.type = null;
this.version = null;
this.helpers = new BCS.Helpers(this);
this.url = (this.address.match(/^http/) ? '' : 'http://') + this.address + (this.address.match(/\/$/) ? '' : '/') + 'api/';
this._callbacks = {};
this.options = options || {};
/* parse auth credentials from URL if present and auth is not set in options */
if(!this.options.auth && this.url.match(/http:\/\/(.+):(.+)@/))
{
parsedAuth = this.url.match(/http:\/\/(.+):(.+)@/).slice(1);
this.options.auth = {
username: parsedAuth[0],
password: parsedAuth[1]
};
this.url = this.url.replace(/(http:\/\/)(.+:.+@)/, '$1');
}
if(this.options.auth) {
this.headers = { Authorization: 'Basic ' + btoa(this.options.auth.username + ":" + this.options.auth.password) };
} else {
this.headers = {};
}
this.read('device')
.then(function (body) {
obj.ready = true;
obj.version = body.version;
obj.type = body.type;
obj.trigger('ready');
})
.catch(function (error) {
obj.trigger('notReady', [error]);
});
return this;
};
Device.prototype = {
get probeCount() { return !this.ready ? null : (this.type === 'BCS-460' ? 4 : 8); },
get inputCount() { return !this.ready ? null : (this.type === 'BCS-460' ? 4 : 8); },
get outputCount() { return !this.ready ? null : (this.type === 'BCS-460' ? 6 : this.type === 'BCS-482' ? 16 : 18); }
};
/**
* Add an event listener
* @param {String} event The event to respond to. ('ready', 'notReady')
* @param {callback} callback The function to be called when the event is triggered
*/
Device.prototype.on = function (event, callback) {
if(this._callbacks[event] === undefined) {
this._callbacks[event] = [callback];
} else {
this._callbacks[event].push(callback);
}
return this;
};
/**
* Trigger an event listener
* @private
* @param {String} event The event to trigger
* @param {Object} arg Argument to pass to the callback
*/
Device.prototype.trigger = function (event, arg) {
var obj = this;
if(this._callbacks[event] !== undefined) {
this._callbacks[event].forEach(function (callback) {
callback.apply(obj, arg);
});
}
};
/**
* Read from the BCS API
* @example
* var bcs = BCS.Device("192.168.0.63");
* bcs.read('device').then(function (response) {
* alert("BCS Name:" + response.name);
* });
* @param {String} resource The API endpoint to query
* @returns {Promise.Object} A Promise of the response from the API
*/
Device.prototype.read = function (resource) {
return fetch(this.url + resource, {headers: this.headers})
.then(function (body) {
return body.json();
});
};
/**
* Write to the BCS API
* @param {String} resource The API endpoint to update
* @param {Object} JSON object to POST to the API
* @returns {Promise.Object} A Promise of the response from the API
*/
Device.prototype.write = function (resource, data) {
return fetch(this.url + resource, {
method: 'POST',
headers: this.headers,
body: JSON.stringify(data)})
.then(function (body) {
return body.json();
});
};
return Device;
}());
BCS.Time = (function () {
/**
* Makes it easier to work with time values from the BCS
* @constructs BCS.Time
* @param {Number} time Time in tenths of a second
*/
var Time = function (time) {
this.value = time / 10 || 0;
return this;
};
var formatNumber = function (n) {
if(n < 10) {
return '0' + n;
}
return "" + n;
};
/**
* Return the string representation of the BCS.Time object
* @returns {String} The string representation of the Time
*/
Time.prototype.toString = function () {
var hours = Math.floor(this.value / 3600);
var minutes = Math.floor((this.value % 3600) / 60);
var seconds = Math.floor(this.value % 60);
return hours + ":" + formatNumber(minutes) + ":" + formatNumber(seconds);
};
/**
* Convert a string into a BCS.Time object
* @example
* var timeobj = BCS.Time(1000);
* BCS.Time.fromString(timeobj.toString()) == timeobj;
*
* @param {String} s The string to convert to BCS.Time in hh:mm:ss format
* @returns {BCS.Time} A BCS.Time object
*/
Time.fromString = function (s) {
var parts = s.split(':').reverse();
var value = 0;
for(var i = 0; i < parts.length; i++) {
// Don't process more than 3 :'s
if(i > 2) { return new Time(value); }
value += parseInt(parts[i]) * Math.pow(60, i);
}
return new Time(value * 10);
};
return Time;
}());