liftstream
Version:
streaming interface for db adam api
133 lines (111 loc) • 4.01 kB
JavaScript
// node modules
var util = require("util");
var path = require("path");
var fs = require("fs");
// npm modules
var debug = require("debug")("elevents");
var request = require("request");
var dur = require("dur");
function elevents(config, fn){
if (!(this instanceof elevents)) return new elevents(config, fn);
var self = this;
self.config = config || {};
// determine default statefile
if (!self.config.hasOwnProperty("statefile") || self.config.statefile) self.config.statefile = path.resolve(path.dirname(require.main.filename), "elevents-state.json");
// load initial state
self.state = {};
fs.exists(self.config.statefile, function(exists){
if (!exists) return debug("starting with empty state") || self.start(fn);
fs.readFile(self.config.statefile, function(err, data){
if (err) return debug("error loading state file: %s", err) || fn(err);
try {
self.state = JSON.parse(data);
} catch(err) {
return debug("error parsing state file: %s", err) || fn(err);
}
self.start(fn);
});
});
// check for log file
self.out = false;
if (self.config.hasOwnProperty("logfile") && typeof self.config.logfile === "string" && self.config.logfile !== "") self.out = fs.createWriteStream(self.config.logfile, { flags: 'a' });
return self;
};
util.inherits(elevents, require("events").EventEmitter);
elevents.prototype.start = function(fn){
var self = this;
if (!self.timer) self.timer = setInterval(function(){
self.update();
}, dur(self.config.interval, 60000));
self.update();
if (typeof fn === "function") fn(null, self);
return self;
};
elevents.prototype.get = function(){
// aggregate initial state
var self = this;
var data = [];
Object.keys(self.state).forEach(function(k){
data.push(self.state[k]);
});
return data;
};
elevents.prototype.stop = function(fn){
var self = this;
if (self.timer) clearInterval(self.timer);
if (typeof fn === "function") fn(null, self);
return self;
};
// retrieve data and fire differences
elevents.prototype.update = function(){
var self = this;
debug("requesting facilities");
request({
method: "GET",
url: "https://adam.noncd.db.de/api/v1.0/facilities",
headers: {"user-agent": "Liftstream/1"} // strangely, the api requires a user agent
}, function(err, resp, data){
if (err) return debug("could not retrieve data: %s", err) || self.emit("error", err);
if (resp.statusCode !== 200) return debug("api returned status code %d", resp.statusCode) || self.emit("error", new Error("API returned status code "+resp.statusCode));
debug("got data");
try {
data = JSON.parse(data);
} catch (err) {
return debug("could not retrieve data: %s", err) || self.emit("error", err);
}
// iterate over data and check for changes
var changed = false;
data.forEach(function(record){
if (!self.state.hasOwnProperty(record.equipmentnumber) || self.state[record.equipmentnumber].state !== record.state) {
// new equipment or state has changed
record.timestamp = (new Date()).valueOf();
self.emit("data", record);
self.state[record.equipmentnumber] = record;
// write log
if (self.out) self.out.write(JSON.stringify(record)+"\n");
changed = true;
}
});
// save current state on change
if (changed) fs.writeFile(self.config.statefile, JSON.stringify(self.state, null, "\t"), function(err){
if (err) return debug("state file could not be written: %s", err);
});
// send dev events if configured
if (!changed && self.config.hasOwnProperty("dev") && self.config.dev === true) {
self.examplestate = (!self.hasOwnProperty("examplestate") || self.examplestate === "INACTIVE") ? "ACTIVE" : "INACTIVE";
self.emit("data", {
"equipmentnumber": 0,
"type": "ELEVATOR",
"description": "Example Elevator",
"geocoordX": 0,
"geocoordY": 0,
"state": self.examplestate,
"stationnumber": 0,
"timestamp": (new Date()).valueOf()
});
}
});
return self;
};
module.exports = elevents;