edbot
Version:
Edbot Node.js API module
409 lines (377 loc) • 9.28 kB
JavaScript
/*
* Edbot Software API Node.js module.
*
* Uses native Promise mechanism for asynchronous processing.
*/
const os = require("os");
const axios = require("axios");
const merge = require("deepmerge");
const WebSocket = require("isomorphic-ws");
class Deferred {
constructor(value) {
if(value !== undefined) {
this.value = value;
}
this.promise = new Promise((resolve, reject) => {
this.reject = reject;
this.resolve = resolve;
});
}
}
class EdbotClient {
///////////////////////////////////////////////////////////////////////////////
//
// Public functions.
//
///////////////////////////////////////////////////////////////////////////////
constructor(server, port, opts) {
this.server = server;
this.port = port;
this.opts = opts;
try {
this.user = os.userInfo().username;
} catch(e) {
this.user = "";
}
this.client = "Node.js";
this.connected = false;
if(opts) {
if(opts.user) {
this.user = opts.user;
}
if(opts.client) {
this.client = opts.client;
}
}
this.deferred = {};
this.deferred.speechComplete = [];
this.deferred.motionComplete = [];
this.deferred.disconnect = new Deferred();
}
//
// Applies to: Edbot, Edbot Dream, Edbot Play&Code.
//
connect() {
if(this.connected) {
throw "Already connected to server";
}
var i = this;
var url = `ws://${i.server}:${i.port}/api/reporter/${i.enc(i.user)}/${i.enc(i.client)}/3`;
return new Promise(function(resolve, reject) {
i.ws = new WebSocket(url);
i.ws.onopen = function(event) {
i.connected = true;
i.data = {};
i.motionSeq = 0;
i.speechSeq = 0;
if(i.opts && i.opts.onopen) {
i.opts.onopen(event);
}
}
i.ws.onmessage = function(event) {
var d = JSON.parse(event.data);
i.data = merge(i.data, d, { arrayMerge: (destinationArray, sourceArray, options) => sourceArray });
if(i.opts && i.opts.onmessage) {
i.opts.onmessage(event);
}
if(i.data.initComplete) {
resolve(i);
}
Object.keys(i.data.robots).forEach(name => {
try {
if(i.data.robots[name].reporters["speechComplete"]) {
if(i.data.robots[name].reporters["speechComplete"] == i.deferred.speechComplete[name].value) {
i.deferred.speechComplete[name].resolve(true);
}
}
if(i.data.robots[name].reporters["motionComplete"]) {
if(i.data.robots[name].reporters["motionComplete"] == i.deferred.motionComplete[name].value) {
i.deferred.motionComplete[name].resolve(true);
}
}
} catch(err) {}
});
};
i.ws.onclose = function(event) {
if(i.connected) {
i.connected = false;
i.data = {};
if(i.opts && i.opts.onclose) {
i.opts.onclose(event);
}
i.deferred.disconnect.resolve(true);
resolve();
}
};
i.ws.onerror = function(e) {
reject(e.message);
};
});
}
//
// Applies to: Edbot, Edbot Dream, Edbot Play&Code.
//
getConnected() {
return this.connected;
}
//
// Applies to: Edbot, Edbot Dream, Edbot Play&Code.
//
disconnect() {
if(!this.connected) {
throw "Not connected to server";
}
this.ws.close(1000, "Closed by client");
return this.deferred.disconnect.promise;
}
//
// Applies to: Edbot, Edbot Dream, Edbot Play&Code.
//
getRobotNames(model) {
if(!this.connected) {
throw "Not connected to server";
}
if(model) {
var result = [];
Object.keys(this.data.robots).forEach(name => {
if(this.data.robots[name].model.key == model) {
result.push(name);
}
});
return result;
} else {
return Object.keys(this.data.robots);
}
}
//
// Applies to: Edbot, Edbot Dream, Edbot Play&Code.
//
getRobot(name) {
if(!this.connected) {
throw "Not connected to server";
}
if(this.data.robots[name]) {
return this.data.robots[name];
} else {
throw(name + " is not configured on this server");
}
}
//
// Applies to: Edbot, Edbot Dream, Edbot Play&Code.
//
getData() {
if(!this.connected) {
throw "Not connected to server";
}
return this.data;
}
//
// Applies to: Edbot, Edbot Dream, Edbot Play&Code.
//
isActive(name) {
if(!this.connected) {
throw "Not connected to server";
}
var robot = this.getRobot(name);
if(robot.activeUser) {
return robot.activeUser == this.data.user;
}
return true;
}
//
// Applies to: Edbot, Edbot Dream, Edbot Play&Code.
//
setServoSpeed(name, path) {
if(!this.connected) {
throw "Not connected to server";
}
return this.apiRequest(`servo_speed/${this.enc(name)}/${path}`);
}
//
// Applies to: Edbot, Edbot Dream, Edbot Play&Code.
//
say(name, text) {
if(!this.connected) {
throw "Not connected to server";
}
var value = this.data.auth + "_s_" + (++this.speechSeq);
this.deferred.speechComplete[name] = new Deferred(value);
var i = this;
var response = null;
return this.apiRequest(`say/${this.enc(name)}/${this.enc(text)}`, { busy: value })
.then(function(res) {
response = res;
if(response.success) {
return i.deferred.speechComplete[name].promise
.then(function() {
return response;
});
} else {
return response;
}
});
}
//
// Applies to: Edbot, Edbot Dream, Edbot Play&Code.
//
reset(name) {
if(!this.connected) {
throw "Not connected to server";
}
return this.apiRequest(`reset/${this.enc(name)}`);
}
//
// Applies to: Edbot, Edbot Dream, Edbot Play&Code.
//
setOptions(name, path) {
if(!this.connected) {
throw "Not connected to server";
}
return this.apiRequest(`options/${this.enc(name)}/${path}`);
}
//
// Applies to: Edbot, Edbot Dream, Edbot Play&Code.
//
setCustom(name, path) {
if(!this.connected) {
throw "Not connected to server";
}
return this.apiRequest(`custom/${this.enc(name)}/${path}`);
}
//
// Applies to: Edbot, Edbot Dream.
//
setServoTorque(name, path) {
if(!this.connected) {
throw "Not connected to server";
}
return this.apiRequest(`servo_torque/${this.enc(name)}/${path}`);
}
//
// Applies to: Edbot, Edbot Dream.
//
setServoPosition(name, path) {
if(!this.connected) {
throw "Not connected to server";
}
return this.apiRequest(`servo_position/${this.enc(name)}/${path}`);
}
//
// Applies to: Edbot Dream, Edbot Play&Code.
//
setBuzzer(name, path) {
if(!this.connected) {
throw "Not connected to server";
}
return this.apiRequest(`buzzer/${this.enc(name)}/${path}`);
}
//
// Applies to: Edbot.
//
getMotions(name) {
if(!this.connected) {
throw "Not connected to server";
}
var url = `http://${this.server}:${this.port}/api/motions/${this.enc(name)}`;
return axios.get(url, { headers: { "X-Edbot-Auth": this.data.auth } })
.then(response => {
return response.data;
});
}
//
// Applies to: Edbot.
//
getDefaultMotions(model) {
if(!this.connected) {
throw "Not connected to server";
}
var url = `http://${this.server}:${this.port}/api/def_motions/${this.enc(model)}`;
return axios.get(url, { headers: { "X-Edbot-Auth": this.data.auth } })
.then(response => {
return response.data;
});
}
//
// Applies to: Edbot.
//
runMotion(name, motionId) {
if(!this.connected) {
throw "Not connected to server";
}
var value = this.data.auth + "_m_" + (++this.motionSeq);
this.deferred.motionComplete[name] = new Deferred(value);
var i = this;
var response = null;
return this.apiRequest(`motion/${this.enc(name)}/${motionId}`, { busy: value })
.then(function(res) {
response = res;
if(response.success) {
return i.deferred.motionComplete[name].promise
.then(function() {
return response;
});
} else {
return response;
}
});
}
//
// Applies to: Edbot.
//
setServoLED(name, path) {
if(!this.connected) {
throw "Not connected to server";
}
return this.apiRequest(`servo_led/${this.enc(name)}/${path}`);
}
//
// Applies to: Edbot.
//
setServoPID(name, path) {
if(!this.connected) {
throw "Not connected to server";
}
return this.apiRequest(`servo_pid/${this.enc(name)}/${path}`);
}
//
// Applies to: Edbot Dream.
//
setServoMode(name, path) {
if(!this.connected) {
throw "Not connected to server";
}
return this.apiRequest(`servo_mode/${this.enc(name)}/${path}`);
}
///////////////////////////////////////////////////////////////////////////////
//
// Private functions.
//
///////////////////////////////////////////////////////////////////////////////
getRemoteServers() {
if(!this.connected) {
throw "Not connected to server";
}
var url = `http://${this.server}:${this.port}/api/remote_servers`;
return axios.get(url, { headers: { "X-Edbot-Auth": this.data.auth } })
.then(response => {
return response.data;
});
}
setServoCombined(name, path) {
if(!this.connected) {
throw "Not connected to server";
}
return this.apiRequest(`servo_combined/${this.enc(name)}/${path}`);
}
apiRequest(path, params) {
var url = `http://${this.server}:${this.port}/api/${path}`;
return axios.get(url, { headers: { "X-Edbot-Auth": this.data.auth }, params: params })
.then(response => {
return response.data.status;
});
}
enc(str) {
return encodeURIComponent(str);
}
}
module.exports = EdbotClient;