Merge branch 'master' of https://gitea.kellenicki.com/nkellenicki/node-poweredup into feature/mario
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Nathan Kellenicki 2020-12-22 15:44:25 -08:00
commit 38bdea2642
13 changed files with 746 additions and 3335 deletions

3935
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -17,20 +17,20 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"compare-versions": "^3.6.0", "compare-versions": "^3.6.0",
"debug": "^4.1.1", "debug": "^4.3.1",
"@abandonware/noble": "1.9.2-9" "@abandonware/noble": "1.9.2-10"
}, },
"devDependencies": { "devDependencies": {
"@types/debug": "4.1.5", "@types/debug": "4.1.5",
"@types/node": "^14.0.13", "@types/node": "^14.14.14",
"@types/web-bluetooth": "0.0.6", "@types/web-bluetooth": "0.0.9",
"ink-docstrap": "^1.3.2", "ink-docstrap": "^1.3.2",
"jsdoc": "^3.6.4", "jsdoc": "^3.6.6",
"jsdoc-to-markdown": "^6.0.1", "jsdoc-to-markdown": "^6.0.1",
"ts-loader": "^7.0.5", "ts-loader": "^8.0.12",
"tslint": "^6.1.2", "tslint": "^6.1.3",
"typescript": "^3.9.5", "typescript": "^4.1.3",
"webpack": "^4.43.0", "webpack": "^5.11.0",
"webpack-cli": "^3.3.11" "webpack-cli": "^4.2.0"
} }
} }

View File

@ -50,7 +50,7 @@ export class AbsoluteMotor extends TachoMotor {
throw new Error("Absolute positioning is not available on the WeDo 2.0 Smart Hub"); throw new Error("Absolute positioning is not available on the WeDo 2.0 Smart Hub");
} }
this.cancelEventTimer(); this.cancelEventTimer();
return new Promise((resolve) => { return new Promise<void>((resolve) => {
this._busy = true; this._busy = true;
if (speed === undefined || speed === null) { if (speed === undefined || speed === null) {
speed = 100; speed = 100;
@ -81,7 +81,7 @@ export class AbsoluteMotor extends TachoMotor {
* @returns {Promise} Resolved upon successful completion of command (ie. once the motor is finished). * @returns {Promise} Resolved upon successful completion of command (ie. once the motor is finished).
*/ */
public gotoRealZero (speed: number = 100) { public gotoRealZero (speed: number = 100) {
return new Promise((resolve) => { return new Promise<void>((resolve) => {
const oldMode = this.mode; const oldMode = this.mode;
let calibrated = false; let calibrated = false;
this.on("absolute", async ({ angle }) => { this.on("absolute", async ({ angle }) => {
@ -110,7 +110,7 @@ export class AbsoluteMotor extends TachoMotor {
* @returns {Promise} Resolved upon successful completion of command (ie. once the motor is finished). * @returns {Promise} Resolved upon successful completion of command (ie. once the motor is finished).
*/ */
public resetZero () { public resetZero () {
return new Promise((resolve) => { return new Promise<void>((resolve) => {
const data = Buffer.from([0x81, this.portId, 0x11, 0x51, 0x02, 0x00, 0x00, 0x00, 0x00]); const data = Buffer.from([0x81, this.portId, 0x11, 0x51, 0x02, 0x00, 0x00, 0x00, 0x00]);
this.send(data); this.send(data);
return resolve(); return resolve();

View File

@ -178,7 +178,7 @@ export class ColorDistanceSensor extends Device {
* @returns {Promise} Resolved upon successful issuance of the command. * @returns {Promise} Resolved upon successful issuance of the command.
*/ */
public setColor (color: number | boolean) { public setColor (color: number | boolean) {
return new Promise((resolve, reject) => { return new Promise<void>((resolve) => {
if (color === false) { if (color === false) {
color = 0; color = 0;
} }
@ -186,7 +186,7 @@ export class ColorDistanceSensor extends Device {
throw new Error("Setting LED color is not available on the WeDo 2.0 Smart Hub"); throw new Error("Setting LED color is not available on the WeDo 2.0 Smart Hub");
} else { } else {
this.subscribe(Mode.LED); this.subscribe(Mode.LED);
this.writeDirect(0x05, Buffer.from([color])); this.writeDirect(0x05, Buffer.from([color as number]));
} }
return resolve(); return resolve();
}); });

View File

@ -21,7 +21,7 @@ export class DuploTrainBaseSpeaker extends Device {
* @returns {Promise} Resolved upon successful issuance of the command. * @returns {Promise} Resolved upon successful issuance of the command.
*/ */
public playSound (sound: Consts.DuploTrainBaseSound) { public playSound (sound: Consts.DuploTrainBaseSound) {
return new Promise((resolve, reject) => { return new Promise<void>((resolve) => {
this.subscribe(Mode.SOUND); this.subscribe(Mode.SOUND);
this.writeDirect(0x01, Buffer.from([sound])); this.writeDirect(0x01, Buffer.from([sound]));
return resolve(); return resolve();

View File

@ -23,7 +23,7 @@ export class HubLED extends Device {
* @returns {Promise} Resolved upon successful issuance of the command. * @returns {Promise} Resolved upon successful issuance of the command.
*/ */
public setColor (color: number | boolean) { public setColor (color: number | boolean) {
return new Promise((resolve, reject) => { return new Promise<void>((resolve) => {
if (typeof color === "boolean") { if (typeof color === "boolean") {
color = 0; color = 0;
} }
@ -48,7 +48,7 @@ export class HubLED extends Device {
* @returns {Promise} Resolved upon successful issuance of the command. * @returns {Promise} Resolved upon successful issuance of the command.
*/ */
public setRGB (red: number, green: number, blue: number) { public setRGB (red: number, green: number, blue: number) {
return new Promise((resolve, reject) => { return new Promise<void>((resolve) => {
if (this.isWeDo2SmartHub) { if (this.isWeDo2SmartHub) {
this.send(Buffer.from([0x06, 0x17, 0x01, 0x02]), Consts.BLECharacteristic.WEDO2_PORT_TYPE_WRITE); this.send(Buffer.from([0x06, 0x17, 0x01, 0x02]), Consts.BLECharacteristic.WEDO2_PORT_TYPE_WRITE);
this.send(Buffer.from([0x06, 0x04, 0x03, red, green, blue]), Consts.BLECharacteristic.WEDO2_MOTOR_VALUE_WRITE); this.send(Buffer.from([0x06, 0x04, 0x03, red, green, blue]), Consts.BLECharacteristic.WEDO2_MOTOR_VALUE_WRITE);

View File

@ -27,7 +27,7 @@ export class Light extends Device {
if (interrupt) { if (interrupt) {
this.cancelEventTimer(); this.cancelEventTimer();
} }
return new Promise((resolve) => { return new Promise<void>((resolve) => {
this.writeDirect(0x00, Buffer.from([brightness])); this.writeDirect(0x00, Buffer.from([brightness]));
return resolve(); return resolve();
}); });

View File

@ -103,7 +103,7 @@ export class TachoMotor extends BasicMotor {
throw new Error("Motor speed is not available on the WeDo 2.0 Smart Hub"); throw new Error("Motor speed is not available on the WeDo 2.0 Smart Hub");
} }
this.cancelEventTimer(); this.cancelEventTimer();
return new Promise((resolve) => { return new Promise<void>((resolve) => {
this._busy = true; this._busy = true;
if (speed === undefined || speed === null) { if (speed === undefined || speed === null) {
speed = 100; speed = 100;
@ -145,7 +145,7 @@ export class TachoMotor extends BasicMotor {
throw new Error("Rotation is not available on the WeDo 2.0 Smart Hub"); throw new Error("Rotation is not available on the WeDo 2.0 Smart Hub");
} }
this.cancelEventTimer(); this.cancelEventTimer();
return new Promise((resolve) => { return new Promise<void>((resolve) => {
this._busy = true; this._busy = true;
if (speed === undefined || speed === null) { if (speed === undefined || speed === null) {
speed = 100; speed = 100;

View File

@ -25,12 +25,12 @@ export class LPF2Hub extends BaseHub {
await super.connect(); await super.connect();
await this._bleDevice.discoverCharacteristicsForService(Consts.BLEService.LPF2_HUB); await this._bleDevice.discoverCharacteristicsForService(Consts.BLEService.LPF2_HUB);
this._bleDevice.subscribeToCharacteristic(Consts.BLECharacteristic.LPF2_ALL, this._parseMessage.bind(this)); this._bleDevice.subscribeToCharacteristic(Consts.BLECharacteristic.LPF2_ALL, this._parseMessage.bind(this));
await this._requestHubPropertyReports(0x02); // Activate button reports await this._requestHubPropertyReports(Consts.HubPropertyPayload.BUTTON_STATE);
await this._requestHubPropertyValue(0x03); // Request firmware version await this._requestHubPropertyValue(Consts.HubPropertyPayload.FW_VERSION);
await this._requestHubPropertyValue(0x04); // Request hardware version await this._requestHubPropertyValue(Consts.HubPropertyPayload.HW_VERSION);
await this._requestHubPropertyReports(0x05); // Activate RSSI updates await this._requestHubPropertyReports(Consts.HubPropertyPayload.RSSI);
await this._requestHubPropertyReports(0x06); // Activate battery level reports await this._requestHubPropertyReports(Consts.HubPropertyPayload.BATTERY_VOLTAGE);
await this._requestHubPropertyValue(0x0d); // Request primary MAC address await this._requestHubPropertyValue(Consts.HubPropertyPayload.PRIMARY_MAC_ADDRESS);
this.emit("connect"); this.emit("connect");
debug("LPF2Hub connected"); debug("LPF2Hub connected");
} }
@ -132,7 +132,7 @@ export class LPF2Hub extends BaseHub {
debug("Received Message (LPF2_ALL)", message); debug("Received Message (LPF2_ALL)", message);
switch (message[2]) { switch (message[2]) {
case 0x01: { case Consts.MessageType.HUB_PROPERTIES: {
const property = message[3]; const property = message[3];
const callback = this._propertyRequestCallbacks[property]; const callback = this._propertyRequestCallbacks[property];
if (callback) { if (callback) {
@ -143,23 +143,23 @@ export class LPF2Hub extends BaseHub {
delete this._propertyRequestCallbacks[property]; delete this._propertyRequestCallbacks[property];
break; break;
} }
case 0x04: { case Consts.MessageType.HUB_ATTACHED_IO: {
this._parsePortMessage(message); this._parsePortMessage(message);
break; break;
} }
case 0x43: { case Consts.MessageType.PORT_INFORMATION: {
this._parsePortInformationResponse(message); this._parsePortInformationResponse(message);
break; break;
} }
case 0x44: { case Consts.MessageType.PORT_MODE_INFORMATION: {
this._parseModeInformationResponse(message); this._parseModeInformationResponse(message);
break; break;
} }
case 0x45: { case Consts.MessageType.PORT_VALUE_SINGLE: {
this._parseSensorMessage(message); this._parseSensorMessage(message);
break; break;
} }
case 0x82: { case Consts.MessageType.HUB_ACTIONS: {
this._parsePortAction(message); this._parsePortAction(message);
break; break;
} }
@ -174,7 +174,7 @@ export class LPF2Hub extends BaseHub {
private _requestHubPropertyValue (property: number) { private _requestHubPropertyValue (property: number) {
return new Promise((resolve) => { return new Promise<void>((resolve) => {
this._propertyRequestCallbacks[property] = (message) => { this._propertyRequestCallbacks[property] = (message) => {
this._parseHubPropertyResponse(message); this._parseHubPropertyResponse(message);
return resolve(); return resolve();
@ -190,9 +190,7 @@ export class LPF2Hub extends BaseHub {
private _parseHubPropertyResponse (message: Buffer) { private _parseHubPropertyResponse (message: Buffer) {
if (message[3] === Consts.HubPropertyPayload.BUTTON_STATE) {
// Button press reports
if (message[3] === 0x02) {
if (message[5] === 1) { if (message[5] === 1) {
/** /**
* Emits when a button is pressed. * Emits when a button is pressed.
@ -207,29 +205,24 @@ export class LPF2Hub extends BaseHub {
return; return;
} }
// Firmware version } else if (message[3] === Consts.HubPropertyPayload.FW_VERSION) {
} else if (message[3] === 0x03) {
this._firmwareVersion = decodeVersion(message.readInt32LE(5)); this._firmwareVersion = decodeVersion(message.readInt32LE(5));
this._checkFirmware(this._firmwareVersion); this._checkFirmware(this._firmwareVersion);
// Hardware version } else if (message[3] === Consts.HubPropertyPayload.HW_VERSION) {
} else if (message[3] === 0x04) {
this._hardwareVersion = decodeVersion(message.readInt32LE(5)); this._hardwareVersion = decodeVersion(message.readInt32LE(5));
// RSSI update } else if (message[3] === Consts.HubPropertyPayload.RSSI) {
} else if (message[3] === 0x05) {
const rssi = message.readInt8(5); const rssi = message.readInt8(5);
if (rssi !== 0) { if (rssi !== 0) {
this._rssi = rssi; this._rssi = rssi;
this.emit("rssi", { rssi: this._rssi }); this.emit("rssi", { rssi: this._rssi });
} }
// primary MAC Address } else if (message[3] === Consts.HubPropertyPayload.PRIMARY_MAC_ADDRESS) {
} else if (message[3] === 0x0d) {
this._primaryMACAddress = decodeMACAddress(message.slice(5)); this._primaryMACAddress = decodeMACAddress(message.slice(5));
// Battery level reports } else if (message[3] === Consts.HubPropertyPayload.BATTERY_VOLTAGE) {
} else if (message[3] === 0x06) {
const batteryLevel = message[5]; const batteryLevel = message[5];
if (batteryLevel !== this._batteryLevel) { if (batteryLevel !== this._batteryLevel) {
this._batteryLevel = batteryLevel; this._batteryLevel = batteryLevel;
@ -245,8 +238,7 @@ export class LPF2Hub extends BaseHub {
const event = message[4]; const event = message[4];
const deviceType = event ? message.readUInt16LE(5) : 0; const deviceType = event ? message.readUInt16LE(5) : 0;
// Handle device attachments if (event === Consts.AlertPayload.ATTACHED_IO) {
if (event === 0x01) {
if (modeInfoDebug.enabled) { if (modeInfoDebug.enabled) {
const deviceTypeName = Consts.DeviceTypeNames[message[5]] || "Unknown"; const deviceTypeName = Consts.DeviceTypeNames[message[5]] || "Unknown";
@ -260,8 +252,7 @@ export class LPF2Hub extends BaseHub {
const device = this._createDevice(deviceType, portId); const device = this._createDevice(deviceType, portId);
this._attachDevice(device); this._attachDevice(device);
// Handle device detachments } else if (event === Consts.AlertPayload.DETACHED_IO) {
} else if (event === 0x00) {
const device = this._getDeviceByPortId(portId); const device = this._getDeviceByPortId(portId);
if (device) { if (device) {
this._detachDevice(device); this._detachDevice(device);
@ -274,8 +265,7 @@ export class LPF2Hub extends BaseHub {
} }
} }
// Handle virtual port creation } else if (event === Consts.AlertPayload.ATTACHED_VIRTUAL_IO) {
} else if (event === 0x02) {
const firstPortName = this.getPortNameForPortId(message[7]); const firstPortName = this.getPortNameForPortId(message[7]);
const secondPortName = this.getPortNameForPortId(message[8]); const secondPortName = this.getPortNameForPortId(message[8]);
// @ts-ignore NK These should never be undefined // @ts-ignore NK These should never be undefined
@ -312,12 +302,12 @@ export class LPF2Hub extends BaseHub {
modeInfoDebug(`Port ${toHex(port)}, total modes ${count}, input modes ${input}, output modes ${output}`); modeInfoDebug(`Port ${toHex(port)}, total modes ${count}, input modes ${input}, output modes ${output}`);
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
await this._sendModeInformationRequest(port, i, 0x00); // Mode Name await this._sendModeInformationRequest(port, i, Consts.ModeInformationType.NAME);
await this._sendModeInformationRequest(port, i, 0x01); // RAW Range await this._sendModeInformationRequest(port, i, Consts.ModeInformationType.RAW);
await this._sendModeInformationRequest(port, i, 0x02); // PCT Range await this._sendModeInformationRequest(port, i, Consts.ModeInformationType.PCT);
await this._sendModeInformationRequest(port, i, 0x03); // SI Range await this._sendModeInformationRequest(port, i, Consts.ModeInformationType.SI);
await this._sendModeInformationRequest(port, i, 0x04); // SI Symbol await this._sendModeInformationRequest(port, i, Consts.ModeInformationType.SYMBOL);
await this._sendModeInformationRequest(port, i, 0x80); // Value Format await this._sendModeInformationRequest(port, i, Consts.ModeInformationType.VALUE_FORMAT);
} }
} }
@ -332,22 +322,22 @@ export class LPF2Hub extends BaseHub {
const mode = message[4]; const mode = message[4];
const type = message[5]; const type = message[5];
switch (type) { switch (type) {
case 0x00: // Mode Name case Consts.ModeInformationType.NAME:
modeInfoDebug(`Port ${port}, mode ${mode}, name ${message.slice(6, message.length).toString()}`); modeInfoDebug(`Port ${port}, mode ${mode}, name ${message.slice(6, message.length).toString()}`);
break; break;
case 0x01: // RAW Range case Consts.ModeInformationType.RAW:
modeInfoDebug(`Port ${port}, mode ${mode}, RAW min ${message.readFloatLE(6)}, max ${message.readFloatLE(10)}`); modeInfoDebug(`Port ${port}, mode ${mode}, RAW min ${message.readFloatLE(6)}, max ${message.readFloatLE(10)}`);
break; break;
case 0x02: // PCT Range case Consts.ModeInformationType.PCT:
modeInfoDebug(`Port ${port}, mode ${mode}, PCT min ${message.readFloatLE(6)}, max ${message.readFloatLE(10)}`); modeInfoDebug(`Port ${port}, mode ${mode}, PCT min ${message.readFloatLE(6)}, max ${message.readFloatLE(10)}`);
break; break;
case 0x03: // SI Range case Consts.ModeInformationType.SI:
modeInfoDebug(`Port ${port}, mode ${mode}, SI min ${message.readFloatLE(6)}, max ${message.readFloatLE(10)}`); modeInfoDebug(`Port ${port}, mode ${mode}, SI min ${message.readFloatLE(6)}, max ${message.readFloatLE(10)}`);
break; break;
case 0x04: // SI Symbol case Consts.ModeInformationType.SYMBOL:
modeInfoDebug(`Port ${port}, mode ${mode}, SI symbol ${message.slice(6, message.length).toString()}`); modeInfoDebug(`Port ${port}, mode ${mode}, SI symbol ${message.slice(6, message.length).toString()}`);
break; break;
case 0x80: // Value Format case Consts.ModeInformationType.VALUE_FORMAT:
const numValues = message[6]; const numValues = message[6];
const dataType = ["8bit", "16bit", "32bit", "float"][message[7]]; const dataType = ["8bit", "16bit", "32bit", "float"][message[7]];
const totalFigures = message[8]; const totalFigures = message[8];

View File

@ -41,7 +41,7 @@ export class WeDo2SmartHub extends BaseHub {
public connect () { public connect () {
return new Promise<void>(async (resolve, reject) => { return new Promise<void>(async (resolve) => {
debug("Connecting to WeDo 2.0 Smart Hub"); debug("Connecting to WeDo 2.0 Smart Hub");
await super.connect(); await super.connect();
await this._bleDevice.discoverCharacteristicsForService(Consts.BLEService.WEDO2_SMART_HUB); await this._bleDevice.discoverCharacteristicsForService(Consts.BLEService.WEDO2_SMART_HUB);
@ -113,7 +113,7 @@ export class WeDo2SmartHub extends BaseHub {
if (name.length > 14) { if (name.length > 14) {
throw new Error("Name must be 14 characters or less"); throw new Error("Name must be 14 characters or less");
} }
return new Promise((resolve, reject) => { return new Promise<void>((resolve) => {
const data = Buffer.from(name, "ascii"); const data = Buffer.from(name, "ascii");
// Send this twice, as sometimes the first time doesn't take // Send this twice, as sometimes the first time doesn't take
this.send(data, Consts.BLECharacteristic.WEDO2_NAME_ID); this.send(data, Consts.BLECharacteristic.WEDO2_NAME_ID);

View File

@ -92,9 +92,9 @@ export class NobleDevice extends EventEmitter implements IBLEAbstraction {
return discoverReject(err); return discoverReject(err);
} }
debug("Service/characteristic discovery started"); debug("Service/characteristic discovery started");
const servicePromises: Promise<null>[] = []; const servicePromises: Promise<void>[] = [];
services.forEach((service) => { services.forEach((service) => {
servicePromises.push(new Promise((resolve, reject) => { servicePromises.push(new Promise((resolve) => {
service.discoverCharacteristics([], (err, characteristics) => { service.discoverCharacteristics([], (err, characteristics) => {
characteristics.forEach((characteristic) => { characteristics.forEach((characteristic) => {
this._characteristics[characteristic.uuid] = characteristic; this._characteristics[characteristic.uuid] = characteristic;

View File

@ -129,7 +129,7 @@ export class PoweredUP extends EventEmitter {
private _determineLPF2HubType (device: IBLEAbstraction): Promise<Consts.HubType> { private _determineLPF2HubType (device: IBLEAbstraction): Promise<Consts.HubType> {
return new Promise((resolve, reject) => { return new Promise((resolve) => {
let buf: Buffer = Buffer.alloc(0); let buf: Buffer = Buffer.alloc(0);
device.subscribeToCharacteristic(Consts.BLECharacteristic.LPF2_ALL, (data: Buffer) => { device.subscribeToCharacteristic(Consts.BLECharacteristic.LPF2_ALL, (data: Buffer) => {
buf = Buffer.concat([buf, data]); buf = Buffer.concat([buf, data]);

View File

@ -14,7 +14,7 @@ export class WebBLEDevice extends EventEmitter implements IBLEAbstraction {
private _listeners: {[uuid: string]: any} = {}; private _listeners: {[uuid: string]: any} = {};
private _characteristics: {[uuid: string]: any} = {}; private _characteristics: {[uuid: string]: any} = {};
private _queue: Promise<any> = Promise.resolve(); private _queue: Promise<void> = Promise.resolve();
private _mailbox: Buffer[] = []; private _mailbox: Buffer[] = [];
private _connected: boolean = false; private _connected: boolean = false;
@ -58,7 +58,7 @@ export class WebBLEDevice extends EventEmitter implements IBLEAbstraction {
public connect () { public connect () {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve) => {
this._connected = true; this._connected = true;
return resolve(); return resolve();
}); });
@ -66,7 +66,7 @@ export class WebBLEDevice extends EventEmitter implements IBLEAbstraction {
public disconnect () { public disconnect () {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve) => {
this._webBLEServer.device.gatt.disconnect(); this._webBLEServer.device.gatt.disconnect();
return resolve(); return resolve();
}); });