zigbee
Version:
ZigBee for Node.JS (using the TI CC2530/CC2531)
134 lines (108 loc) • 3.53 kB
JavaScript
/**
* Parses XML ZigBee profile/cluster data from "data/profiles"
* so it can be searched when devices are discovered.
*/
var parser = new require('xml2js').Parser();
var fs = require('fs');
var util = require('util');
var stream = require('stream');
var _ = require('underscore');
var async = require('async');
var debug = require('debug')('ProfileStore');
util.inherits(ProfileStore,stream);
function ProfileStore(fileNames) {
var self = this;
this._profiles = [];
var tasks = [ function(cb) {
self.readXml(__dirname + '/../../data/profiles/zcl.xml', function(err, result) {
debug('Parsed ' + result.clusters.cluster.length + ' clusters from the ZCL');
self._clusters = _.map(result.clusters.cluster, cleanHorribleOutput);
cb();
});
}];
_.each(fileNames, function(fileName) {
tasks.push(function(cb) {
self.readXml(__dirname + '/../../data/profiles/' + fileName + '.xml', function(err, result) {
debug('Parsed ' + result.profiles.profile.length + ' profiles from ' + fileName + '.xml');
self._profiles = self._profiles.concat(_.map(result.profiles.profile, cleanHorribleOutput));
cb();
});
});
});
async.parallel(tasks, function() {
// Attach the clusters and profile id to the device
_.each(self._profiles, function(profile) {
_.each(profile.device, function(device) {
device.profile = profile.id;
_.each(['server', 'client'], function(section) {
if (device[section]) {
device[section] = _.map(device[section][0].clusterRef, function(clusterRef) {
return self.getClusterByName(clusterRef.name);
});
}
});
});
});
debug('Ready');
self.emit('ready');
});
}
ProfileStore.prototype.getDevice = function(profileId, deviceId) {
if (typeof profileId == 'number') profileId = hex(profileId);
if (typeof deviceId == 'number') deviceId = hex(deviceId);
var profile = this.getProfile(profileId);
if (!profile) {
debug('Profile ' + profileId + ' not found.');
return null;
}
return this.filterById(profile.device, deviceId);
};
ProfileStore.prototype.getCluster = function(clusterId) {
if (typeof clusterId == 'number') clusterId = hex(clusterId);
return _.filter(this._clusters, function(c) {
return c.id == clusterId;
})[0];
};
ProfileStore.prototype.getClusterByName = function(name) {
return _.filter(this._clusters, function(c) {
return c.name == name;
})[0];
};
ProfileStore.prototype.getProfile = function(id) {
return this.filterById(this._profiles, id);
};
ProfileStore.prototype.filterById = function(haystack, id) {
var str = typeof id == 'string';
return _.filter(haystack, function(p) {
return str? p.id == id : parseInt(p.id, 16) == id;
})[0];
};
ProfileStore.prototype.readXml = function(file, cb) {
var self = this;
debug('Parsing ' + file);
fs.readFile(file, function(err, data) {
parser.parseString(data, function (err, result) {
cb(err, result);
});
});
};
module.exports = ProfileStore;
// Ignore me
// ES: I hate all the stupid xml attribute properties... so I'm flattening.
function cleanHorribleOutput(x) {
if (x && x.$) {
_.extend(x, x.$);
delete(x.$);
}
_.each(x, function(value, prop) {
if (_.isArray(value) || _.isObject(value)) {
cleanHorribleOutput(value);
}
});
return x;
}
function hex(v) {
v = '000' + v.toString(16);
return '0x' + v.substring(v.length-4);
}
;