@iotile/iotile-device
Version:
A typescript library for interfacing with IOTile BLE devices
380 lines (373 loc) • 18.2 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
var iotile_common_1 = require("@iotile/iotile-common");
var error_space_1 = require("../common/error-space");
var BridgeEnum;
(function (BridgeEnum) {
BridgeEnum[BridgeEnum["Connection"] = 1] = "Connection";
BridgeEnum[BridgeEnum["Address"] = 2] = "Address";
BridgeEnum[BridgeEnum["RemoteUsername"] = 4] = "RemoteUsername";
BridgeEnum[BridgeEnum["RemotePassword"] = 5] = "RemotePassword";
BridgeEnum[BridgeEnum["Topic"] = 6] = "Topic";
BridgeEnum[BridgeEnum["TryPrivate"] = 7] = "TryPrivate";
BridgeEnum[BridgeEnum["TLS"] = 8] = "TLS";
})(BridgeEnum = exports.BridgeEnum || (exports.BridgeEnum = {}));
var TLSEnum;
(function (TLSEnum) {
TLSEnum[TLSEnum["CAFile"] = 0] = "CAFile";
TLSEnum[TLSEnum["CertFile"] = 1] = "CertFile";
TLSEnum[TLSEnum["KeyFile"] = 2] = "KeyFile";
})(TLSEnum = exports.TLSEnum || (exports.TLSEnum = {}));
/**
* A proxy object to configure a local MQTT broker that is in bridge mode
*/
var MQTTBridgeConfig = /** @class */ (function () {
function MQTTBridgeConfig(adapter, address) {
this.adapter = adapter;
this.address = address;
}
/**
* Brokers currently supported must be running with anonymous connections allowed,
or require a username and/or password for auth. If you are using TLS, you should
have sent the required files over using the TLS methods in this proxy first.
Please note that we currently only support a single TLS connected broker. If you
overwrite the TLS files that another connection is using, you will break it.
You can add connections that have either plain or password authentication in any quantity.
* @param {string} name Unique connection name.
* @param {string} url IP or URL that the bridged broker is running on
* @param {number} port Port used by the bridged broker. Defaults to 1883. Must be numeric.
* @param {string} remoteUsername Optional username if the bridge requires one
* @param {string} remotePassword Optional password if the bridge requires one
* @param {boolean} tls Optionally set TLS mode. Send certificates first.
*/
MQTTBridgeConfig.prototype.addBridge = function (name, url, port, remoteUsername, remotePassword, tls) {
if (port === void 0) { port = 1883; }
if (remoteUsername === void 0) { remoteUsername = null; }
if (remotePassword === void 0) { remotePassword = null; }
if (tls === void 0) { tls = null; }
return __awaiter(this, void 0, void 0, function () {
var offset, done, end, err_1, err_2;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (typeof port !== 'number') {
throw new iotile_common_1.ArgumentError('Error, your port must contain only numbers. Please try again.');
}
return [4 /*yield*/, this._clearPendingBridge()
// Add name
];
case 1:
_a.sent();
offset = 0;
done = 0;
_a.label = 2;
case 2:
if (!!done) return [3 /*break*/, 7];
end = Math.min(offset + 19, name.length);
if (end === name.length) {
done = 1;
}
_a.label = 3;
case 3:
_a.trys.push([3, 5, , 6]);
return [4 /*yield*/, this.adapter.errorHandlingRPC(this.address, 0xAA02, 'BV', 'L', [done, iotile_common_1.stringToBuffer(name.slice(offset, end))])];
case 4:
_a.sent();
return [3 /*break*/, 6];
case 5:
err_1 = _a.sent();
if (err_1 instanceof error_space_1.RPCError && err_1.errorCode === 2) {
throw new iotile_common_1.ArgumentError('Error, that connection name is already taken, please try again.');
}
else {
throw err_1;
}
return [3 /*break*/, 6];
case 6:
offset += 19;
return [3 /*break*/, 2];
case 7:
// Add url:port combo
return [4 /*yield*/, this._buildBridge(BridgeEnum.Address, url + ':' + port)];
case 8:
// Add url:port combo
_a.sent();
if ((tls && remoteUsername) || (tls && remotePassword)) {
throw new iotile_common_1.ArgumentError('Cannot set both TLS and login mode');
}
if (!remoteUsername) return [3 /*break*/, 10];
return [4 /*yield*/, this._buildBridge(BridgeEnum.RemoteUsername, remoteUsername)];
case 9:
_a.sent();
_a.label = 10;
case 10:
if (!remotePassword) return [3 /*break*/, 12];
return [4 /*yield*/, this._buildBridge(BridgeEnum.RemotePassword, remotePassword)];
case 11:
_a.sent();
_a.label = 12;
case 12:
if (!tls) return [3 /*break*/, 14];
return [4 /*yield*/, this._buildBridge(BridgeEnum.TLS, 'on')];
case 13:
_a.sent();
_a.label = 14;
case 14:
_a.trys.push([14, 16, , 17]);
return [4 /*yield*/, this.adapter.errorHandlingRPC(this.address, 0xAA04, '', 'B', [])];
case 15:
_a.sent();
return [3 /*break*/, 17];
case 16:
err_2 = _a.sent();
if (err_2 instanceof error_space_1.RPCError && err_2.errorCode === 2) {
throw new iotile_common_1.ArgumentError('Error committing bridge, you set TLS but the certificates were incomplete.');
}
else {
throw err_2;
}
return [3 /*break*/, 17];
case 17: return [2 /*return*/];
}
});
});
};
/**
* Remove an existing bridge by name
* @param {string} bridgeName Name of the bridge that you wish to remove
*/
MQTTBridgeConfig.prototype.removeBridge = function (bridgeName) {
return __awaiter(this, void 0, void 0, function () {
var offset, done, end, err_3;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
offset = 0;
done = 0;
_a.label = 1;
case 1:
if (!!done) return [3 /*break*/, 6];
end = Math.min(offset + 19, bridgeName.length);
if (end === bridgeName.length) {
done = 1;
}
_a.label = 2;
case 2:
_a.trys.push([2, 4, , 5]);
return [4 /*yield*/, this.adapter.errorHandlingRPC(this.address, 0xAA05, 'BV', 'B', [done, iotile_common_1.stringToBuffer(bridgeName.slice(offset, end))])];
case 3:
_a.sent();
return [3 /*break*/, 5];
case 4:
err_3 = _a.sent();
if (err_3 instanceof error_space_1.RPCError && err_3.errorCode === 2) {
throw new iotile_common_1.ArgumentError('Bridge not removed because the name doesn\'t exist, please double check spelling.');
}
else if (err_3.errorCode !== 1) { // errorCode 1 is actually success
console.log("ERR:", err_3);
throw err_3;
}
return [3 /*break*/, 5];
case 5:
offset += 19;
return [3 /*break*/, 1];
case 6: return [2 /*return*/];
}
});
});
};
/**
* Your CA certificate. Needs to be pem encoded. Will overwrite an existing CA certificate.
* @param {string} source The source data file sent as a string
*/
MQTTBridgeConfig.prototype.sendCertfile = function (source) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this._sendTLSFile(source, TLSEnum.CertFile)];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
};
/**
* The CA file is typically obtained from your certificate authority. Will overwrite an existing CA file.
It might show up with a name like rootCA.pem
* @param {string} source The source data file sent as a string
*/
MQTTBridgeConfig.prototype.sendCAFile = function (source) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this._sendTLSFile(source, TLSEnum.CAFile)];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
};
/**
* The actual text content of the entire mosquitto configuration file.
Right now, this just contains bridge info, but in the future might also contain security settings,
certificate file locations for various bridges, and other configurable items.
@returns {Promise<string>} The full mosquitto config
*/
MQTTBridgeConfig.prototype.listFullMosquittoConfig = function () {
return __awaiter(this, void 0, void 0, function () {
var offset, full, res;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
offset = 0;
full = '';
return [4 /*yield*/, this.adapter.typedRPC(this.address, 0xAA00, 'L', 'V', [offset])];
case 1:
res = (_a.sent())[0];
if (!res) return [3 /*break*/, 4];
full += res;
_a.label = 2;
case 2:
if (!(res && res.length === 20)) return [3 /*break*/, 4];
offset += 20;
return [4 /*yield*/, this.adapter.typedRPC(this.address, 0xAA00, 'L', 'V', [offset])];
case 3:
res = (_a.sent())[0];
full += res;
return [3 /*break*/, 2];
case 4: return [2 /*return*/, full];
}
});
});
};
/**
* Your private keyfile. Should be pem encoded. Will overwrite an existing keyfile.
* @param {string} source The source data file sent as a string
*/
MQTTBridgeConfig.prototype.sendKeyFile = function (source) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this._sendTLSFile(source, TLSEnum.KeyFile)];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
};
MQTTBridgeConfig.prototype._clearPendingBridge = function () {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.adapter.typedRPC(this.address, 0xAA06, '', '', [])];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
};
/**
* Builds all enumerated bridge setting values
*/
MQTTBridgeConfig.prototype._buildBridge = function (itemId, itemData) {
return __awaiter(this, void 0, void 0, function () {
var offset, done, end;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
offset = 0;
done = 0;
_a.label = 1;
case 1:
if (!!done) return [3 /*break*/, 3];
end = Math.min(offset + 18, itemData.length);
if (end === itemData.length) {
done = 1;
}
return [4 /*yield*/, this.adapter.typedRPC(this.address, 0xAA03, 'BBV', '', [itemId, done, iotile_common_1.stringToBuffer(itemData.slice(offset, end))])];
case 2:
_a.sent();
offset += 18;
return [3 /*break*/, 1];
case 3: return [2 /*return*/];
}
});
});
};
/**
*
* @param source The source data file sent as a string
*/
MQTTBridgeConfig.prototype._sendTLSFile = function (source, tlsFileType) {
return __awaiter(this, void 0, void 0, function () {
var offset, done, end;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.adapter.typedRPC(this.address, 0xAA10, '', '', [])];
case 1:
_a.sent(); // clear
return [4 /*yield*/, this.adapter.typedRPC(this.address, 0xAA11, 'B', '', [tlsFileType])];
case 2:
_a.sent(); // set which file
offset = 0;
done = 0;
_a.label = 3;
case 3:
if (!!done) return [3 /*break*/, 5];
end = Math.min(offset + 20, source.length);
if (end === source.length) {
done = 1;
}
return [4 /*yield*/, this.adapter.typedRPC(this.address, 0xAA13, 'V', '', [iotile_common_1.stringToBuffer(source.slice(offset, end))])];
case 4:
_a.sent();
offset += 20;
return [3 /*break*/, 3];
case 5: return [2 /*return*/];
}
});
});
};
return MQTTBridgeConfig;
}());
exports.MQTTBridgeConfig = MQTTBridgeConfig;
//# sourceMappingURL=iotile-mqtt.js.map