Added autosubscribe when adding eventlisteners

This commit is contained in:
Nathan Kellenicki 2019-12-06 17:31:12 -08:00
parent d453fe52fb
commit 4af1d3d69b
13 changed files with 1194 additions and 1403 deletions

View File

@ -0,0 +1,43 @@
/*
*
* This demonstrates connecting multiple hubs to your laptop. Once connected, all the hubs LED lights will cycle through the same colors simultaneously.
*
*/
const PoweredUP = require("..");
const poweredUP = new PoweredUP.PoweredUP();
poweredUP.scan(); // Start scanning for hubs
console.log("Looking for Hubs...");
poweredUP.on("discover", async (hub) => { // Wait to discover hubs
await hub.connect(); // Connect to hub
console.log(`Connected to ${hub.name}!`);
hub.on("attach", (device) => {
if (device instanceof PoweredUP.ControlPlusLargeMotor) {
const motor = device;
motor.setSpeed(30);
}
if (device instanceof PoweredUP.ColorDistanceSensor) {
const sensor = device;
sensor.on("distance", (distance) => { // Adding an event handler for distance automatically subscribes to distance notifications
console.log(`Distance ${distance}`);
})
}
device.on("detach", () => {
console.log(device.connected);
})
});
hub.on("disconnect", () => {
console.log("Hub disconnected");
})
});

View File

@ -3,6 +3,8 @@ import { Hub } from "./hub";
import * as Consts from "./consts"; import * as Consts from "./consts";
import { mapSpeed } from "./utils";
export class BasicMotor extends Device { export class BasicMotor extends Device {
@ -19,7 +21,7 @@ export class BasicMotor extends Device {
*/ */
public setSpeed (speed: number) { public setSpeed (speed: number) {
return new Promise((resolve) => { return new Promise((resolve) => {
const data = Buffer.from([0x81, this.portId, 0x11, 0x51, 0x00, speed]); const data = Buffer.from([0x81, this.portId, 0x11, 0x51, 0x00, mapSpeed(speed)]);
this.send(data); this.send(data);
return resolve(); return resolve();
}); });

View File

@ -36,17 +36,14 @@ export class BoostMoveHub extends LPF2Hub {
constructor (device: IBLEAbstraction, autoSubscribe: boolean = true) { constructor (device: IBLEAbstraction, autoSubscribe: boolean = true) {
super(device, autoSubscribe); super(device, autoSubscribe);
this.type = Consts.HubType.BOOST_MOVE_HUB; this._type = Consts.HubType.BOOST_MOVE_HUB;
this._ports = { this._portNames = {
"A": new Port("A", 0), "A": 0,
"B": new Port("B", 1), "B": 1,
"C": new Port("C", 2), "C": 2,
"D": new Port("D", 3), "D": 3,
"TILT": new Port("TILT", 58) "TILT": 58
}; };
this.on("attach", (port, type) => {
this._combinePorts(port, type);
});
debug("Discovered Boost Move Hub"); debug("Discovered Boost Move Hub");
} }
@ -61,253 +58,253 @@ export class BoostMoveHub extends LPF2Hub {
} }
/** // /**
* Set the motor speed on a given port. // * Set the motor speed on a given port.
* @method BoostMoveHub#setMotorSpeed // * @method BoostMoveHub#setMotorSpeed
* @param {string} port // * @param {string} port
* @param {number | Array.<number>} speed For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0. If you are specifying port AB to control both motors, you can optionally supply a tuple of speeds. // * @param {number | Array.<number>} speed For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0. If you are specifying port AB to control both motors, you can optionally supply a tuple of speeds.
* @param {number} [time] How long to activate the motor for (in milliseconds). Leave empty to turn the motor on indefinitely. // * @param {number} [time] How long to activate the motor for (in milliseconds). Leave empty to turn the motor on indefinitely.
* @returns {Promise} Resolved upon successful completion of command. If time is specified, this is once the motor is finished. // * @returns {Promise} Resolved upon successful completion of command. If time is specified, this is once the motor is finished.
*/ // */
public setMotorSpeed (port: string, speed: number | [number, number], time?: number | boolean) { // public setMotorSpeed (port: string, speed: number | [number, number], time?: number | boolean) {
const portObj = this._portLookup(port); // const portObj = this._portLookup(port);
if (!this._virtualPorts[portObj.id] && speed instanceof Array) { // if (!this._virtualPorts[portObj.id] && speed instanceof Array) {
throw new Error(`Port ${portObj.id} can only accept a single speed`); // throw new Error(`Port ${portObj.id} can only accept a single speed`);
} // }
let cancelEventTimer = true; // let cancelEventTimer = true;
if (typeof time === "boolean") { // if (typeof time === "boolean") {
if (time === true) { // if (time === true) {
cancelEventTimer = false; // cancelEventTimer = false;
} // }
time = undefined; // time = undefined;
} // }
if (cancelEventTimer) { // if (cancelEventTimer) {
portObj.cancelEventTimer(); // portObj.cancelEventTimer();
} // }
return new Promise((resolve, reject) => { // return new Promise((resolve, reject) => {
if (time && typeof time === "number") { // if (time && typeof time === "number") {
if ( // if (
portObj.type === Consts.DeviceType.BOOST_TACHO_MOTOR || // portObj.type === Consts.DeviceType.BOOST_TACHO_MOTOR ||
portObj.type === Consts.DeviceType.BOOST_MOVE_HUB_MOTOR || // portObj.type === Consts.DeviceType.BOOST_MOVE_HUB_MOTOR ||
portObj.type === Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR || // portObj.type === Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR ||
portObj.type === Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR // portObj.type === Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR
) { // ) {
portObj.busy = true; // portObj.busy = true;
let data = null; // let data = null;
if (this._virtualPorts[portObj.id]) { // if (this._virtualPorts[portObj.id]) {
data = Buffer.from([0x81, portObj.value, 0x11, 0x0a, 0x00, 0x00, this._mapSpeed(speed instanceof Array ? speed[0] : speed), this._mapSpeed(speed instanceof Array ? speed[1] : speed), 0x64, 0x7f, 0x03]); // data = Buffer.from([0x81, portObj.value, 0x11, 0x0a, 0x00, 0x00, this._mapSpeed(speed instanceof Array ? speed[0] : speed), this._mapSpeed(speed instanceof Array ? speed[1] : speed), 0x64, 0x7f, 0x03]);
} else { // } else {
// @ts-ignore: The type of speed is properly checked at the start // // @ts-ignore: The type of speed is properly checked at the start
data = Buffer.from([0x81, portObj.value, 0x11, 0x09, 0x00, 0x00, this._mapSpeed(speed), 0x64, 0x7f, 0x03]); // data = Buffer.from([0x81, portObj.value, 0x11, 0x09, 0x00, 0x00, this._mapSpeed(speed), 0x64, 0x7f, 0x03]);
} // }
data.writeUInt16LE(time > 65535 ? 65535 : time, 4); // data.writeUInt16LE(time > 65535 ? 65535 : time, 4);
this.send(Consts.BLECharacteristic.LPF2_ALL, data); // this.send(data, Consts.BLECharacteristic.LPF2_ALL);
portObj.finished = () => { // portObj.finished = () => {
return resolve(); // return resolve();
}; // };
} else { // } else {
// @ts-ignore: The type of speed is properly checked at the start // // @ts-ignore: The type of speed is properly checked at the start
const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, this._mapSpeed(speed)]); // const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, this._mapSpeed(speed)]);
this.send(Consts.BLECharacteristic.LPF2_ALL, data); // this.send(data, Consts.BLECharacteristic.LPF2_ALL);
const timeout = global.setTimeout(() => { // const timeout = global.setTimeout(() => {
const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, 0x00]); // const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, 0x00]);
this.send(Consts.BLECharacteristic.LPF2_ALL, data); // this.send(data, Consts.BLECharacteristic.LPF2_ALL);
return resolve(); // return resolve();
// @ts-ignore: The type of time is properly checked at the start // // @ts-ignore: The type of time is properly checked at the start
}, time); // }, time);
portObj.setEventTimer(timeout); // portObj.setEventTimer(timeout);
} // }
} else { // } else {
if ( // if (
portObj.type === Consts.DeviceType.BOOST_TACHO_MOTOR || // portObj.type === Consts.DeviceType.BOOST_TACHO_MOTOR ||
portObj.type === Consts.DeviceType.BOOST_MOVE_HUB_MOTOR || // portObj.type === Consts.DeviceType.BOOST_MOVE_HUB_MOTOR ||
portObj.type === Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR || // portObj.type === Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR ||
portObj.type === Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR // portObj.type === Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR
) { // ) {
portObj.busy = true; // portObj.busy = true;
let data = null; // let data = null;
if (this._virtualPorts[portObj.id]) { // if (this._virtualPorts[portObj.id]) {
data = Buffer.from([0x81, portObj.value, 0x11, 0x02, this._mapSpeed(speed instanceof Array ? speed[0] : speed), this._mapSpeed(speed instanceof Array ? speed[1] : speed), 0x64, 0x7f, 0x03]); // data = Buffer.from([0x81, portObj.value, 0x11, 0x02, this._mapSpeed(speed instanceof Array ? speed[0] : speed), this._mapSpeed(speed instanceof Array ? speed[1] : speed), 0x64, 0x7f, 0x03]);
} else { // } else {
// @ts-ignore: The type of speed is properly checked at the start // // @ts-ignore: The type of speed is properly checked at the start
data = Buffer.from([0x81, portObj.value, 0x11, 0x01, this._mapSpeed(speed), 0x64, 0x7f, 0x03]); // data = Buffer.from([0x81, portObj.value, 0x11, 0x01, this._mapSpeed(speed), 0x64, 0x7f, 0x03]);
} // }
this.send(Consts.BLECharacteristic.LPF2_ALL, data); // this.send(data, Consts.BLECharacteristic.LPF2_ALL);
portObj.finished = () => { // portObj.finished = () => {
return resolve(); // return resolve();
}; // };
} else { // } else {
// @ts-ignore: The type of speed is properly checked at the start // // @ts-ignore: The type of speed is properly checked at the start
const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, this._mapSpeed(speed)]); // const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, this._mapSpeed(speed)]);
this.send(Consts.BLECharacteristic.LPF2_ALL, data); // this.send(data, Consts.BLECharacteristic.LPF2_ALL);
} // }
} // }
}); // });
} // }
/** // /**
* Ramp the motor speed on a given port. // * Ramp the motor speed on a given port.
* @method BoostMoveHub#rampMotorSpeed // * @method BoostMoveHub#rampMotorSpeed
* @param {string} port // * @param {string} port
* @param {number} fromSpeed For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0. // * @param {number} fromSpeed For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0.
* @param {number} toSpeed For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0. // * @param {number} toSpeed For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0.
* @param {number} time How long the ramp should last (in milliseconds). // * @param {number} time How long the ramp should last (in milliseconds).
* @returns {Promise} Resolved upon successful completion of command. // * @returns {Promise} Resolved upon successful completion of command.
*/ // */
public rampMotorSpeed (port: string, fromSpeed: number, toSpeed: number, time: number) { // public rampMotorSpeed (port: string, fromSpeed: number, toSpeed: number, time: number) {
const portObj = this._portLookup(port); // const portObj = this._portLookup(port);
portObj.cancelEventTimer(); // portObj.cancelEventTimer();
return new Promise((resolve, reject) => { // return new Promise((resolve, reject) => {
this._calculateRamp(fromSpeed, toSpeed, time, portObj) // this._calculateRamp(fromSpeed, toSpeed, time, portObj)
.on("changeSpeed", (speed) => { // .on("changeSpeed", (speed) => {
this.setMotorSpeed(port, speed, true); // this.setMotorSpeed(port, speed, true);
}) // })
.on("finished", resolve); // .on("finished", resolve);
}); // });
} // }
/** // /**
* Rotate a motor by a given angle. // * Rotate a motor by a given angle.
* @method BoostMoveHub#setMotorAngle // * @method BoostMoveHub#setMotorAngle
* @param {string} port // * @param {string} port
* @param {number} angle How much the motor should be rotated (in degrees). // * @param {number} angle How much the motor should be rotated (in degrees).
* @param {number | Array.<number>} [speed=100] For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0. If you are specifying port AB to control both motors, you can optionally supply a tuple of speeds. // * @param {number | Array.<number>} [speed=100] For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0. If you are specifying port AB to control both motors, you can optionally supply a tuple of speeds.
* @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 setMotorAngle (port: string, angle: number, speed: number | [number, number] = 100) { // public setMotorAngle (port: string, angle: number, speed: number | [number, number] = 100) {
const portObj = this._portLookup(port); // const portObj = this._portLookup(port);
if (!( // if (!(
portObj.type === Consts.DeviceType.BOOST_TACHO_MOTOR || // portObj.type === Consts.DeviceType.BOOST_TACHO_MOTOR ||
portObj.type === Consts.DeviceType.BOOST_MOVE_HUB_MOTOR || // portObj.type === Consts.DeviceType.BOOST_MOVE_HUB_MOTOR ||
portObj.type === Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR || // portObj.type === Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR ||
portObj.type === Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR // portObj.type === Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR
)) { // )) {
throw new Error("Angle rotation is only available when using a Boost Tacho Motor, Boost Move Hub Motor, Control+ Medium Motor, or Control+ Large Motor"); // throw new Error("Angle rotation is only available when using a Boost Tacho Motor, Boost Move Hub Motor, Control+ Medium Motor, or Control+ Large Motor");
} // }
if (!this._virtualPorts[portObj.id] && speed instanceof Array) { // if (!this._virtualPorts[portObj.id] && speed instanceof Array) {
throw new Error(`Port ${portObj.id} can only accept a single speed`); // throw new Error(`Port ${portObj.id} can only accept a single speed`);
} // }
portObj.cancelEventTimer(); // portObj.cancelEventTimer();
return new Promise((resolve, reject) => { // return new Promise((resolve, reject) => {
portObj.busy = true; // portObj.busy = true;
let data = null; // let data = null;
if (this._virtualPorts[portObj.id]) { // if (this._virtualPorts[portObj.id]) {
data = Buffer.from([0x81, portObj.value, 0x11, 0x0c, 0x00, 0x00, 0x00, 0x00, this._mapSpeed(speed instanceof Array ? speed[0] : speed), this._mapSpeed(speed instanceof Array ? speed[1] : speed), 0x64, 0x7f, 0x03]); // data = Buffer.from([0x81, portObj.value, 0x11, 0x0c, 0x00, 0x00, 0x00, 0x00, this._mapSpeed(speed instanceof Array ? speed[0] : speed), this._mapSpeed(speed instanceof Array ? speed[1] : speed), 0x64, 0x7f, 0x03]);
} else { // } else {
// @ts-ignore: The type of speed is properly checked at the start // // @ts-ignore: The type of speed is properly checked at the start
data = Buffer.from([0x81, portObj.value, 0x11, 0x0b, 0x00, 0x00, 0x00, 0x00, this._mapSpeed(speed), 0x64, 0x7f, 0x03]); // data = Buffer.from([0x81, portObj.value, 0x11, 0x0b, 0x00, 0x00, 0x00, 0x00, this._mapSpeed(speed), 0x64, 0x7f, 0x03]);
} // }
data.writeUInt32LE(angle, 4); // data.writeUInt32LE(angle, 4);
this.send(Consts.BLECharacteristic.LPF2_ALL, data); // this.send(data, Consts.BLECharacteristic.LPF2_ALL);
portObj.finished = () => { // portObj.finished = () => {
return resolve(); // return resolve();
}; // };
}); // });
} // }
/** // /**
* Tell motor to goto an absolute position // * Tell motor to goto an absolute position
* @method BoostMoveHub#setAbsolutePosition // * @method BoostMoveHub#setAbsolutePosition
* @param {string} port // * @param {string} port
* @param {number} pos The position of the motor to go to // * @param {number} pos The position of the motor to go to
* @param {number | Array.<number>} [speed=100] A value between 1 - 100 should be set (Direction does not apply when going to absolute position) // * @param {number | Array.<number>} [speed=100] A value between 1 - 100 should be set (Direction does not apply when going to absolute position)
* @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 setAbsolutePosition (port: string, pos: number, speed: number = 100) { // public setAbsolutePosition (port: string, pos: number, speed: number = 100) {
const portObj = this._portLookup(port); // const portObj = this._portLookup(port);
if (!( // if (!(
portObj.type === Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR || // portObj.type === Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR ||
portObj.type === Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR // portObj.type === Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR
)) { // )) {
throw new Error("Absolute positioning is only available when using a Control+ Medium Motor, or Control+ Large Motor"); // throw new Error("Absolute positioning is only available when using a Control+ Medium Motor, or Control+ Large Motor");
} // }
portObj.cancelEventTimer(); // portObj.cancelEventTimer();
return new Promise((resolve, reject) => { // return new Promise((resolve, reject) => {
portObj.busy = true; // portObj.busy = true;
let data = null; // let data = null;
if (this._virtualPorts[portObj.id]) { // if (this._virtualPorts[portObj.id]) {
data = Buffer.from([0x81, portObj.value, 0x11, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, this._mapSpeed(speed), 0x64, 0x7f, 0x03]); // data = Buffer.from([0x81, portObj.value, 0x11, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, this._mapSpeed(speed), 0x64, 0x7f, 0x03]);
data.writeInt32LE(pos, 4); // data.writeInt32LE(pos, 4);
data.writeInt32LE(pos, 8); // data.writeInt32LE(pos, 8);
} else { // } else {
// @ts-ignore: The type of speed is properly checked at the start // // @ts-ignore: The type of speed is properly checked at the start
data = Buffer.from([0x81, portObj.value, 0x11, 0x0d, 0x00, 0x00, 0x00, 0x00, this._mapSpeed(speed), 0x64, 0x7f, 0x03]); // data = Buffer.from([0x81, portObj.value, 0x11, 0x0d, 0x00, 0x00, 0x00, 0x00, this._mapSpeed(speed), 0x64, 0x7f, 0x03]);
data.writeInt32LE(pos, 4); // data.writeInt32LE(pos, 4);
} // }
this.send(Consts.BLECharacteristic.LPF2_ALL, data); // this.send(data, Consts.BLECharacteristic.LPF2_ALL);
portObj.finished = () => { // portObj.finished = () => {
return resolve(); // return resolve();
}; // };
}); // });
} // }
/** // /**
* Reset the current motor position as absolute position zero // * Reset the current motor position as absolute position zero
* @method BoostMoveHub#resetAbsolutePosition // * @method BoostMoveHub#resetAbsolutePosition
* @param {string} port // * @param {string} port
* @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 resetAbsolutePosition (port: string) { // public resetAbsolutePosition (port: string) {
const portObj = this._portLookup(port); // const portObj = this._portLookup(port);
if (!( // if (!(
portObj.type === Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR || // portObj.type === Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR ||
portObj.type === Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR // portObj.type === Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR
)) { // )) {
throw new Error("Absolute positioning is only available when using a Control+ Medium Motor, or Control+ Large Motor"); // throw new Error("Absolute positioning is only available when using a Control+ Medium Motor, or Control+ Large Motor");
} // }
return new Promise((resolve) => { // return new Promise((resolve) => {
const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x02, 0x00, 0x00, 0x00, 0x00]); // const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x02, 0x00, 0x00, 0x00, 0x00]);
this.send(Consts.BLECharacteristic.LPF2_ALL, data); // this.send(data, Consts.BLECharacteristic.LPF2_ALL);
return resolve(); // return resolve();
}); // });
} // }
/** // /**
* Fully (hard) stop the motor on a given port. // * Fully (hard) stop the motor on a given port.
* @method BoostMoveHub#brakeMotor // * @method BoostMoveHub#brakeMotor
* @param {string} port // * @param {string} port
* @returns {Promise} Resolved upon successful completion of command. // * @returns {Promise} Resolved upon successful completion of command.
*/ // */
public brakeMotor (port: string) { // public brakeMotor (port: string) {
return this.setMotorSpeed(port, 127); // return this.setMotorSpeed(port, 127);
} // }
/** // /**
* Set the light brightness on a given port. // * Set the light brightness on a given port.
* @method BoostMoveHub#setLightBrightness // * @method BoostMoveHub#setLightBrightness
* @param {string} port // * @param {string} port
* @param {number} brightness Brightness value between 0-100 (0 is off) // * @param {number} brightness Brightness value between 0-100 (0 is off)
* @param {number} [time] How long to turn the light on (in milliseconds). Leave empty to turn the light on indefinitely. // * @param {number} [time] How long to turn the light on (in milliseconds). Leave empty to turn the light on indefinitely.
* @returns {Promise} Resolved upon successful completion of command. If time is specified, this is once the light is turned off. // * @returns {Promise} Resolved upon successful completion of command. If time is specified, this is once the light is turned off.
*/ // */
public setLightBrightness (port: string, brightness: number, time?: number) { // public setLightBrightness (port: string, brightness: number, time?: number) {
const portObj = this._portLookup(port); // const portObj = this._portLookup(port);
portObj.cancelEventTimer(); // portObj.cancelEventTimer();
return new Promise((resolve, reject) => { // return new Promise((resolve, reject) => {
const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, brightness]); // const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, brightness]);
this.send(Consts.BLECharacteristic.LPF2_ALL, data); // this.send(data, Consts.BLECharacteristic.LPF2_ALL);
if (time) { // if (time) {
const timeout = global.setTimeout(() => { // const timeout = global.setTimeout(() => {
const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, 0x00]); // const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, 0x00]);
this.send(Consts.BLECharacteristic.LPF2_ALL, data); // this.send(data, Consts.BLECharacteristic.LPF2_ALL);
return resolve(); // return resolve();
}, time); // }, time);
portObj.setEventTimer(timeout); // portObj.setEventTimer(timeout);
} else { // } else {
return resolve(); // return resolve();
} // }
}); // });
} // }
protected _checkFirmware (version: string) { protected _checkFirmware (version: string) {

View File

@ -7,6 +7,19 @@ export class ColorDistanceSensor extends Device {
constructor (hub: Hub, portId: number) { constructor (hub: Hub, portId: number) {
super(hub, portId, Consts.DeviceType.COLOR_DISTANCE_SENSOR); super(hub, portId, Consts.DeviceType.COLOR_DISTANCE_SENSOR);
this.on("newListener", (event) => {
switch (event) {
case "color":
this.subscribe(0x00);
break;
case "distance":
this.subscribe(0x01);
break;
}
});
} }
} }

View File

@ -38,19 +38,19 @@ export class ControlPlusHub extends LPF2Hub {
constructor (device: IBLEAbstraction, autoSubscribe: boolean = true) { constructor (device: IBLEAbstraction, autoSubscribe: boolean = true) {
super(device, autoSubscribe); super(device, autoSubscribe);
this.type = Consts.HubType.CONTROL_PLUS_HUB; this._type = Consts.HubType.CONTROL_PLUS_HUB;
this._ports = { this._portNames = {
"A": new Port("A", 0), "A": 0,
"B": new Port("B", 1), "B": 1,
"C": new Port("C", 2), "C": 2,
"D": new Port("D", 3), "D": 3,
"ACCEL": new Port("ACCEL", 97), "ACCEL": 97,
"GYRO": new Port("GYRO", 98), "GYRO": 98,
"TILT": new Port("TILT", 99) "TILT": 99
}; };
this.on("attach", (port, type) => { // // this.on("attach", (port, type) => {
this._combinePorts(port, type); // // this._combinePorts(port, type);
}); // // });
debug("Discovered Control+ Hub"); debug("Discovered Control+ Hub");
} }
@ -59,7 +59,7 @@ export class ControlPlusHub extends LPF2Hub {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
debug("Connecting to Control+ Hub"); debug("Connecting to Control+ Hub");
await super.connect(); await super.connect();
this.send(Consts.BLECharacteristic.LPF2_ALL, Buffer.from([0x41, 0x3d, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01])); // Temperature this.send(Buffer.from([0x41, 0x3d, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01]), Consts.BLECharacteristic.LPF2_ALL); // Temperature
debug("Connect completed"); debug("Connect completed");
return resolve(); return resolve();
}); });
@ -75,200 +75,200 @@ export class ControlPlusHub extends LPF2Hub {
* @returns {Promise} Resolved upon successful completion of command. If time is specified, this is once the motor is finished. * @returns {Promise} Resolved upon successful completion of command. If time is specified, this is once the motor is finished.
*/ */
public setMotorSpeed (port: string, speed: number | [number, number], time?: number | boolean) { public setMotorSpeed (port: string, speed: number | [number, number], time?: number | boolean) {
const portObj = this._portLookup(port); // const portObj = this._portLookup(port);
if (!this._virtualPorts[portObj.id] && speed instanceof Array) { // if (!this._virtualPorts[portObj.id] && speed instanceof Array) {
throw new Error(`Port ${portObj.id} can only accept a single speed`); // throw new Error(`Port ${portObj.id} can only accept a single speed`);
} // }
let cancelEventTimer = true; // let cancelEventTimer = true;
if (typeof time === "boolean") { // if (typeof time === "boolean") {
if (time === true) { // if (time === true) {
cancelEventTimer = false; // cancelEventTimer = false;
} // }
time = undefined; // time = undefined;
} // }
if (cancelEventTimer) { // if (cancelEventTimer) {
portObj.cancelEventTimer(); // portObj.cancelEventTimer();
} // }
return new Promise((resolve, reject) => { // return new Promise((resolve, reject) => {
if (time && typeof time === "number") { // if (time && typeof time === "number") {
if ( // if (
portObj.type === Consts.DeviceType.BOOST_TACHO_MOTOR || // portObj.type === Consts.DeviceType.BOOST_TACHO_MOTOR ||
portObj.type === Consts.DeviceType.BOOST_MOVE_HUB_MOTOR || // portObj.type === Consts.DeviceType.BOOST_MOVE_HUB_MOTOR ||
portObj.type === Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR || // portObj.type === Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR ||
portObj.type === Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR // portObj.type === Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR
) { // ) {
portObj.busy = true; // portObj.busy = true;
let data = null; // let data = null;
if (this._virtualPorts[portObj.id]) { // if (this._virtualPorts[portObj.id]) {
data = Buffer.from([0x81, portObj.value, 0x11, 0x0a, 0x00, 0x00, this._mapSpeed(speed instanceof Array ? speed[0] : speed), this._mapSpeed(speed instanceof Array ? speed[1] : speed), 0x64, 0x7f, 0x03]); // data = Buffer.from([0x81, portObj.value, 0x11, 0x0a, 0x00, 0x00, this._mapSpeed(speed instanceof Array ? speed[0] : speed), this._mapSpeed(speed instanceof Array ? speed[1] : speed), 0x64, 0x7f, 0x03]);
} else { // } else {
// @ts-ignore: The type of speed is properly checked at the start // // @ts-ignore: The type of speed is properly checked at the start
data = Buffer.from([0x81, portObj.value, 0x11, 0x09, 0x00, 0x00, this._mapSpeed(speed), 0x64, 0x7f, 0x03]); // data = Buffer.from([0x81, portObj.value, 0x11, 0x09, 0x00, 0x00, this._mapSpeed(speed), 0x64, 0x7f, 0x03]);
} // }
data.writeUInt16LE(time > 65535 ? 65535 : time, 4); // data.writeUInt16LE(time > 65535 ? 65535 : time, 4);
this.send(Consts.BLECharacteristic.LPF2_ALL, data); // this.send(data, Consts.BLECharacteristic.LPF2_ALL);
portObj.finished = () => { // portObj.finished = () => {
return resolve(); // return resolve();
}; // };
} else { // } else {
// @ts-ignore: The type of speed is properly checked at the start // // @ts-ignore: The type of speed is properly checked at the start
const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, this._mapSpeed(speed)]); // const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, this._mapSpeed(speed)]);
this.send(Consts.BLECharacteristic.LPF2_ALL, data); // this.send(data, Consts.BLECharacteristic.LPF2_ALL);
const timeout = global.setTimeout(() => { // const timeout = global.setTimeout(() => {
const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, 0x00]); // const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, 0x00]);
this.send(Consts.BLECharacteristic.LPF2_ALL, data); // this.send(data, Consts.BLECharacteristic.LPF2_ALL);
return resolve(); // return resolve();
// @ts-ignore: The type of time is properly checked at the start // // @ts-ignore: The type of time is properly checked at the start
}, time); // }, time);
portObj.setEventTimer(timeout); // portObj.setEventTimer(timeout);
} // }
} else { // } else {
if (portObj.type === Consts.DeviceType.BOOST_TACHO_MOTOR || portObj.type === Consts.DeviceType.BOOST_MOVE_HUB_MOTOR) { // if (portObj.type === Consts.DeviceType.BOOST_TACHO_MOTOR || portObj.type === Consts.DeviceType.BOOST_MOVE_HUB_MOTOR) {
portObj.busy = true; // portObj.busy = true;
let data = null; // let data = null;
if (this._virtualPorts[portObj.id]) { // if (this._virtualPorts[portObj.id]) {
data = Buffer.from([0x81, portObj.value, 0x11, 0x02, this._mapSpeed(speed instanceof Array ? speed[0] : speed), this._mapSpeed(speed instanceof Array ? speed[1] : speed), 0x64, 0x7f, 0x03]); // data = Buffer.from([0x81, portObj.value, 0x11, 0x02, this._mapSpeed(speed instanceof Array ? speed[0] : speed), this._mapSpeed(speed instanceof Array ? speed[1] : speed), 0x64, 0x7f, 0x03]);
} else { // } else {
// @ts-ignore: The type of speed is properly checked at the start // // @ts-ignore: The type of speed is properly checked at the start
data = Buffer.from([0x81, portObj.value, 0x11, 0x01, this._mapSpeed(speed), 0x64, 0x7f, 0x03]); // data = Buffer.from([0x81, portObj.value, 0x11, 0x01, this._mapSpeed(speed), 0x64, 0x7f, 0x03]);
} // }
this.send(Consts.BLECharacteristic.LPF2_ALL, data); // this.send(data, Consts.BLECharacteristic.LPF2_ALL);
portObj.finished = () => { // portObj.finished = () => {
return resolve(); // return resolve();
}; // };
} else { // } else {
// @ts-ignore: The type of speed is properly checked at the start // // @ts-ignore: The type of speed is properly checked at the start
const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, this._mapSpeed(speed)]); // const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, this._mapSpeed(speed)]);
this.send(Consts.BLECharacteristic.LPF2_ALL, data); // this.send(data, Consts.BLECharacteristic.LPF2_ALL);
} // }
} // }
}); // });
} }
/** // /**
* Ramp the motor speed on a given port. // * Ramp the motor speed on a given port.
* @method ControlPlusHub#rampMotorSpeed // * @method ControlPlusHub#rampMotorSpeed
* @param {string} port // * @param {string} port
* @param {number} fromSpeed For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0. // * @param {number} fromSpeed For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0.
* @param {number} toSpeed For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0. // * @param {number} toSpeed For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0.
* @param {number} time How long the ramp should last (in milliseconds). // * @param {number} time How long the ramp should last (in milliseconds).
* @returns {Promise} Resolved upon successful completion of command. // * @returns {Promise} Resolved upon successful completion of command.
*/ // */
public rampMotorSpeed (port: string, fromSpeed: number, toSpeed: number, time: number) { // public rampMotorSpeed (port: string, fromSpeed: number, toSpeed: number, time: number) {
const portObj = this._portLookup(port); // const portObj = this._portLookup(port);
portObj.cancelEventTimer(); // portObj.cancelEventTimer();
return new Promise((resolve, reject) => { // return new Promise((resolve, reject) => {
this._calculateRamp(fromSpeed, toSpeed, time, portObj) // this._calculateRamp(fromSpeed, toSpeed, time, portObj)
.on("changeSpeed", (speed) => { // .on("changeSpeed", (speed) => {
this.setMotorSpeed(port, speed, true); // this.setMotorSpeed(port, speed, true);
}) // })
.on("finished", resolve); // .on("finished", resolve);
}); // });
} // }
/** // /**
* Rotate a motor by a given angle. // * Rotate a motor by a given angle.
* @method ControlPlusHub#setMotorAngle // * @method ControlPlusHub#setMotorAngle
* @param {string} port // * @param {string} port
* @param {number} angle How much the motor should be rotated (in degrees). // * @param {number} angle How much the motor should be rotated (in degrees).
* @param {number | Array.<number>} [speed=100] For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0. If you are specifying port AB to control both motors, you can optionally supply a tuple of speeds. // * @param {number | Array.<number>} [speed=100] For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0. If you are specifying port AB to control both motors, you can optionally supply a tuple of speeds.
* @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 setMotorAngle (port: string, angle: number, speed: number | [number, number] = 100) { // public setMotorAngle (port: string, angle: number, speed: number | [number, number] = 100) {
const portObj = this._portLookup(port); // const portObj = this._portLookup(port);
if (!( // if (!(
portObj.type === Consts.DeviceType.BOOST_TACHO_MOTOR || // portObj.type === Consts.DeviceType.BOOST_TACHO_MOTOR ||
portObj.type === Consts.DeviceType.BOOST_MOVE_HUB_MOTOR || // portObj.type === Consts.DeviceType.BOOST_MOVE_HUB_MOTOR ||
portObj.type === Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR || // portObj.type === Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR ||
portObj.type === Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR // portObj.type === Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR
)) { // )) {
throw new Error("Angle rotation is only available when using a Boost Tacho Motor, Boost Move Hub Motor, Control+ Medium Motor, or Control+ Large Motor"); // throw new Error("Angle rotation is only available when using a Boost Tacho Motor, Boost Move Hub Motor, Control+ Medium Motor, or Control+ Large Motor");
} // }
if (!this._virtualPorts[portObj.id] && speed instanceof Array) { // if (!this._virtualPorts[portObj.id] && speed instanceof Array) {
throw new Error(`Port ${portObj.id} can only accept a single speed`); // throw new Error(`Port ${portObj.id} can only accept a single speed`);
} // }
portObj.cancelEventTimer(); // portObj.cancelEventTimer();
return new Promise((resolve, reject) => { // return new Promise((resolve, reject) => {
portObj.busy = true; // portObj.busy = true;
let data = null; // let data = null;
if (this._virtualPorts[portObj.id]) { // if (this._virtualPorts[portObj.id]) {
data = Buffer.from([0x81, portObj.value, 0x11, 0x0c, 0x00, 0x00, 0x00, 0x00, this._mapSpeed(speed instanceof Array ? speed[0] : speed), this._mapSpeed(speed instanceof Array ? speed[1] : speed), 0x64, 0x7f, 0x03]); // data = Buffer.from([0x81, portObj.value, 0x11, 0x0c, 0x00, 0x00, 0x00, 0x00, this._mapSpeed(speed instanceof Array ? speed[0] : speed), this._mapSpeed(speed instanceof Array ? speed[1] : speed), 0x64, 0x7f, 0x03]);
} else { // } else {
// @ts-ignore: The type of speed is properly checked at the start // // @ts-ignore: The type of speed is properly checked at the start
data = Buffer.from([0x81, portObj.value, 0x11, 0x0b, 0x00, 0x00, 0x00, 0x00, this._mapSpeed(speed), 0x64, 0x7f, 0x03]); // data = Buffer.from([0x81, portObj.value, 0x11, 0x0b, 0x00, 0x00, 0x00, 0x00, this._mapSpeed(speed), 0x64, 0x7f, 0x03]);
} // }
data.writeUInt32LE(angle, 4); // data.writeUInt32LE(angle, 4);
this.send(Consts.BLECharacteristic.LPF2_ALL, data); // this.send(data, Consts.BLECharacteristic.LPF2_ALL);
portObj.finished = () => { // portObj.finished = () => {
return resolve(); // return resolve();
}; // };
}); // });
} // }
/** // /**
* Tell motor to goto an absolute position // * Tell motor to goto an absolute position
* @method ControlPlusHub#setAbsolutePosition // * @method ControlPlusHub#setAbsolutePosition
* @param {string} port // * @param {string} port
* @param {number} pos The position of the motor to go to // * @param {number} pos The position of the motor to go to
* @param {number | Array.<number>} [speed=100] A value between 1 - 100 should be set (Direction does not apply when going to absolute position) // * @param {number | Array.<number>} [speed=100] A value between 1 - 100 should be set (Direction does not apply when going to absolute position)
* @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 setAbsolutePosition (port: string, pos: number, speed: number = 100) { // public setAbsolutePosition (port: string, pos: number, speed: number = 100) {
const portObj = this._portLookup(port); // const portObj = this._portLookup(port);
if (!( // if (!(
portObj.type === Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR || // portObj.type === Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR ||
portObj.type === Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR // portObj.type === Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR
)) { // )) {
throw new Error("Absolute positioning is only available when using a Control+ Medium Motor, or Control+ Large Motor"); // throw new Error("Absolute positioning is only available when using a Control+ Medium Motor, or Control+ Large Motor");
} // }
portObj.cancelEventTimer(); // portObj.cancelEventTimer();
return new Promise((resolve, reject) => { // return new Promise((resolve, reject) => {
portObj.busy = true; // portObj.busy = true;
let data = null; // let data = null;
if (this._virtualPorts[portObj.id]) { // if (this._virtualPorts[portObj.id]) {
data = Buffer.from([0x81, portObj.value, 0x11, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, this._mapSpeed(speed), 0x64, 0x7f, 0x03]); // data = Buffer.from([0x81, portObj.value, 0x11, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, this._mapSpeed(speed), 0x64, 0x7f, 0x03]);
data.writeInt32LE(pos, 4); // data.writeInt32LE(pos, 4);
data.writeInt32LE(pos, 8); // data.writeInt32LE(pos, 8);
} else { // } else {
// @ts-ignore: The type of speed is properly checked at the start // // @ts-ignore: The type of speed is properly checked at the start
data = Buffer.from([0x81, portObj.value, 0x11, 0x0d, 0x00, 0x00, 0x00, 0x00, this._mapSpeed(speed), 0x64, 0x7f, 0x03]); // data = Buffer.from([0x81, portObj.value, 0x11, 0x0d, 0x00, 0x00, 0x00, 0x00, this._mapSpeed(speed), 0x64, 0x7f, 0x03]);
data.writeInt32LE(pos, 4); // data.writeInt32LE(pos, 4);
} // }
this.send(Consts.BLECharacteristic.LPF2_ALL, data); // this.send(data, Consts.BLECharacteristic.LPF2_ALL);
portObj.finished = () => { // portObj.finished = () => {
return resolve(); // return resolve();
}; // };
}); // });
} // }
/** // /**
* Reset the current motor position as absolute position zero // * Reset the current motor position as absolute position zero
* @method ControlPlusHub#resetAbsolutePosition // * @method ControlPlusHub#resetAbsolutePosition
* @param {string} port // * @param {string} port
* @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 resetAbsolutePosition (port: string) { // public resetAbsolutePosition (port: string) {
const portObj = this._portLookup(port); // const portObj = this._portLookup(port);
if (!( // if (!(
portObj.type === Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR || // portObj.type === Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR ||
portObj.type === Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR // portObj.type === Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR
)) { // )) {
throw new Error("Absolute positioning is only available when using a Control+ Medium Motor, or Control+ Large Motor"); // throw new Error("Absolute positioning is only available when using a Control+ Medium Motor, or Control+ Large Motor");
} // }
return new Promise((resolve) => { // return new Promise((resolve) => {
const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x02, 0x00, 0x00, 0x00, 0x00]); // const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x02, 0x00, 0x00, 0x00, 0x00]);
this.send(Consts.BLECharacteristic.LPF2_ALL, data); // this.send(data, Consts.BLECharacteristic.LPF2_ALL);
return resolve(); // return resolve();
}); // });
} // }
/** /**
@ -282,32 +282,32 @@ export class ControlPlusHub extends LPF2Hub {
} }
/** // /**
* Set the light brightness on a given port. // * Set the light brightness on a given port.
* @method ControlPlusHub#setLightBrightness // * @method ControlPlusHub#setLightBrightness
* @param {string} port // * @param {string} port
* @param {number} brightness Brightness value between 0-100 (0 is off) // * @param {number} brightness Brightness value between 0-100 (0 is off)
* @param {number} [time] How long to turn the light on (in milliseconds). Leave empty to turn the light on indefinitely. // * @param {number} [time] How long to turn the light on (in milliseconds). Leave empty to turn the light on indefinitely.
* @returns {Promise} Resolved upon successful completion of command. If time is specified, this is once the light is turned off. // * @returns {Promise} Resolved upon successful completion of command. If time is specified, this is once the light is turned off.
*/ // */
public setLightBrightness (port: string, brightness: number, time?: number) { // public setLightBrightness (port: string, brightness: number, time?: number) {
const portObj = this._portLookup(port); // const portObj = this._portLookup(port);
portObj.cancelEventTimer(); // portObj.cancelEventTimer();
return new Promise((resolve, reject) => { // return new Promise((resolve, reject) => {
const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, brightness]); // const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, brightness]);
this.send(Consts.BLECharacteristic.LPF2_ALL, data); // this.send(data, Consts.BLECharacteristic.LPF2_ALL);
if (time) { // if (time) {
const timeout = global.setTimeout(() => { // const timeout = global.setTimeout(() => {
const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, 0x00]); // const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, 0x00]);
this.send(Consts.BLECharacteristic.LPF2_ALL, data); // this.send(data, Consts.BLECharacteristic.LPF2_ALL);
return resolve(); // return resolve();
}, time); // }, time);
portObj.setEventTimer(timeout); // portObj.setEventTimer(timeout);
} else { // } else {
return resolve(); // return resolve();
} // }
}); // });
} // }
} }

View File

@ -9,13 +9,20 @@ export class Device extends EventEmitter {
private _portId: number; private _portId: number;
private _connected: boolean = true; private _connected: boolean = true;
private _type: number; private _type: number;
private _busy: boolean = false;
private _finished: (() => void) | null = null;
constructor (hub: Hub, portId: number, type: number = Consts.DeviceType.UNKNOWN) { constructor (hub: Hub, portId: number, type: number = Consts.DeviceType.UNKNOWN) {
super(); super();
this._hub = hub; this._hub = hub;
this._portId = portId; this._portId = portId;
this._type = type; this._type = type;
console.log(`New device on ${this._portId} - ${this._type}`); this.hub.on("detach", (device) => {
if (device.portId === this.portId) {
this._connected = false;
this.emit("detach");
}
});
} }
public get connected () { public get connected () {
@ -31,7 +38,7 @@ export class Device extends EventEmitter {
} }
public get port () { public get port () {
return "A"; // TODO NK: Look up the port name from the relevant hub return this.hub.getPortNameForPortId(this.portId);
} }
public get type () { public get type () {
@ -39,7 +46,11 @@ export class Device extends EventEmitter {
} }
public send (data: Buffer, characteristic: string = Consts.BLECharacteristic.LPF2_ALL, callback?: () => void) { public send (data: Buffer, characteristic: string = Consts.BLECharacteristic.LPF2_ALL, callback?: () => void) {
this.hub.send(characteristic, data, callback); this.hub.send(data, characteristic, callback);
}
public subscribe (mode: number) {
this.send(Buffer.from([0x41, this.portId, mode, 0x01, 0x00, 0x00, 0x00, 0x01]));
} }
} }

View File

@ -38,11 +38,11 @@ export class DuploTrainBase extends LPF2Hub {
constructor (device: IBLEAbstraction, autoSubscribe: boolean = true) { constructor (device: IBLEAbstraction, autoSubscribe: boolean = true) {
super(device, autoSubscribe); super(device, autoSubscribe);
this.type = Consts.HubType.DUPLO_TRAIN_HUB; this._type = Consts.HubType.DUPLO_TRAIN_HUB;
this._ports = { this._portNames = {
"MOTOR": new Port("MOTOR", 0), "MOTOR": 0,
"COLOR": new Port("COLOR", 18), "COLOR": 18,
"SPEEDOMETER": new Port("SPEEDOMETER", 19) "SPEEDOMETER": 19
}; };
debug("Discovered Duplo Train Base"); debug("Discovered Duplo Train Base");
} }
@ -57,77 +57,6 @@ export class DuploTrainBase extends LPF2Hub {
}); });
} }
/**
* Set the motor speed on a given port.
* @method DuploTrainBase#setMotorSpeed
* @param {string} port
* @param {number | Array.<number>} speed For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0. If you are specifying port AB to control both motors, you can optionally supply a tuple of speeds.
* @param {number} [time] How long to activate the motor for (in milliseconds). Leave empty to turn the motor on indefinitely.
* @returns {Promise} Resolved upon successful completion of command. If time is specified, this is once the motor is finished.
*/
public setMotorSpeed (port: string, speed: number, time?: number | boolean) {
const portObj = this._portLookup(port);
let cancelEventTimer = true;
if (typeof time === "boolean") {
if (time === true) {
cancelEventTimer = false;
}
time = undefined;
}
if (cancelEventTimer) {
portObj.cancelEventTimer();
}
return new Promise((resolve, reject) => {
if (time && typeof time === "number") {
const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, this._mapSpeed(speed)]);
this.send(Consts.BLECharacteristic.LPF2_ALL, data);
const timeout = global.setTimeout(() => {
const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, 0x00]);
this.send(Consts.BLECharacteristic.LPF2_ALL, data);
return resolve();
}, time);
portObj.setEventTimer(timeout);
} else {
const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, this._mapSpeed(speed)]);
this.send(Consts.BLECharacteristic.LPF2_ALL, data);
return resolve();
}
});
}
/**
* Ramp the motor speed on a given port.
* @method DuploTrainBase#rampMotorSpeed
* @param {string} port
* @param {number} fromSpeed For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0.
* @param {number} toSpeed For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0.
* @param {number} time How long the ramp should last (in milliseconds).
* @returns {Promise} Resolved upon successful completion of command.
*/
public rampMotorSpeed (port: string, fromSpeed: number, toSpeed: number, time: number) {
const portObj = this._portLookup(port);
portObj.cancelEventTimer();
return new Promise((resolve, reject) => {
this._calculateRamp(fromSpeed, toSpeed, time, portObj)
.on("changeSpeed", (speed) => {
this.setMotorSpeed(port, speed, true);
})
.on("finished", resolve);
});
}
/**
* Fully (hard) stop the motor on a given port.
* @method DuploTrainBase#brakeMotor
* @param {string} port
* @returns {Promise} Resolved upon successful completion of command.
*/
public brakeMotor (port: string) {
return this.setMotorSpeed(port, 127);
}
/** /**
* Play a built-in train sound. * Play a built-in train sound.
@ -138,7 +67,7 @@ export class DuploTrainBase extends LPF2Hub {
public playSound (sound: number) { public playSound (sound: number) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const data = Buffer.from([0x81, 0x01, 0x11, 0x51, 0x01, sound]); const data = Buffer.from([0x81, 0x01, 0x11, 0x51, 0x01, sound]);
this.send(Consts.BLECharacteristic.LPF2_ALL, data); this.send(data, Consts.BLECharacteristic.LPF2_ALL);
return resolve(); return resolve();
}); });
} }

View File

@ -19,12 +19,12 @@ export class Hub extends EventEmitter {
public autoSubscribe: boolean = true; public autoSubscribe: boolean = true;
public useSpeedMap: boolean = true; public useSpeedMap: boolean = true;
public type: Consts.HubType = Consts.HubType.UNKNOWN; protected _type: Consts.HubType = Consts.HubType.UNKNOWN;
protected _attachedDevices: Device[] = []; protected _attachedDevices: Device[] = [];
protected _ports: {[port: string]: Port} = {}; protected _portNames: {[port: string]: number} = {};
protected _virtualPorts: {[port: string]: Port} = {}; // protected _virtualPorts: {[port: string]: Port} = {};
protected _name: string = ""; protected _name: string = "";
protected _firmwareVersion: string = "0.0.00.0000"; protected _firmwareVersion: string = "0.0.00.0000";
@ -63,6 +63,15 @@ export class Hub extends EventEmitter {
} }
/**
* @readonly
* @property {string} type Hub type
*/
public get type () {
return this._type;
}
/** /**
* @readonly * @readonly
* @property {string} firmwareVersion Firmware version of the hub * @property {string} firmwareVersion Firmware version of the hub
@ -160,44 +169,55 @@ export class Hub extends EventEmitter {
* @method Hub#disconnect * @method Hub#disconnect
* @returns {Promise} Resolved upon successful disconnect. * @returns {Promise} Resolved upon successful disconnect.
*/ */
public async disconnect () { public disconnect () {
this._bleDevice.disconnect(); return this._bleDevice.disconnect();
} }
/** public getPortNameForPortId (portId: number) {
* Subscribe to sensor notifications on a given port. for (const port of Object.keys(this._portNames)) {
* @method Hub#subscribe console.log(port);
* @param {string} port if (this._portNames[port] === portId) {
* @param {number} [mode] The sensor mode to activate. If no mode is provided, the default for that sensor will be chosen. return port;
* @returns {Promise} Resolved upon successful issuance of command.
*/
public subscribe (port: string, mode?: number) {
return new Promise((resolve, reject) => {
let newMode = this._getModeForDeviceType(this._portLookup(port).type);
if (mode !== undefined) {
newMode = mode;
} }
this._activatePortDevice(this._portLookup(port).value, this._portLookup(port).type, newMode, 0x00, () => { }
return resolve(); return;
});
});
} }
/**
* Unsubscribe to sensor notifications on a given port. // /**
* @method Hub#unsubscribe // * Subscribe to sensor notifications on a given port.
* @param {string} port // * @method Hub#subscribe
* @returns {Promise} Resolved upon successful issuance of command. // * @param {string} port
*/ // * @param {number} [mode] The sensor mode to activate. If no mode is provided, the default for that sensor will be chosen.
public unsubscribe (port: string) { // * @returns {Promise} Resolved upon successful issuance of command.
return new Promise((resolve, reject) => { // */
const mode = this._getModeForDeviceType(this._portLookup(port).type); // public subscribe (port: string, mode?: number) {
this._deactivatePortDevice(this._portLookup(port).value, this._portLookup(port).type, mode, 0x00, () => { // return new Promise((resolve, reject) => {
return resolve(); // let newMode = this._getModeForDeviceType(this._portLookup(port).type);
}); // if (mode !== undefined) {
}); // newMode = mode;
} // }
// this._activatePortDevice(this._portLookup(port).value, this._portLookup(port).type, newMode, 0x00, () => {
// return resolve();
// });
// });
// }
// /**
// * Unsubscribe to sensor notifications on a given port.
// * @method Hub#unsubscribe
// * @param {string} port
// * @returns {Promise} Resolved upon successful issuance of command.
// */
// public unsubscribe (port: string) {
// return new Promise((resolve, reject) => {
// const mode = this._getModeForDeviceType(this._portLookup(port).type);
// this._deactivatePortDevice(this._portLookup(port).value, this._portLookup(port).type, mode, 0x00, () => {
// return resolve();
// });
// });
// }
/** /**
@ -228,28 +248,18 @@ export class Hub extends EventEmitter {
} }
/** // /**
* Get the hub type. // * Get the device type for a given port.
* @method Hub#getHubType // * @method Hub#getPortDeviceType
* @returns {HubType} // * @param {string} port
*/ // * @returns {DeviceType}
public getHubType () { // */
return this.type; // public getPortDeviceType (port: string) {
} // return this._portLookup(port).type;
// }
/** public send (message: Buffer, uuid: string, callback?: () => void) {
* Get the device type for a given port.
* @method Hub#getPortDeviceType
* @param {string} port
* @returns {DeviceType}
*/
public getPortDeviceType (port: string) {
return this._portLookup(port).type;
}
public send (uuid: string, message: Buffer, callback?: () => void) {
if (callback) { if (callback) {
callback(); callback();
} }
@ -273,26 +283,12 @@ export class Hub extends EventEmitter {
// } // }
protected _activatePortDevice (port: number, type: number, mode: number, format: number, callback?: () => void) { protected _attachDevice (device: Device) {
if (callback) {
callback();
}
}
const exists = this._getDeviceByPortId(device.portId);
protected _deactivatePortDevice (port: number, type: number, mode: number, format: number, callback?: () => void) {
if (callback) {
callback();
}
}
protected _registerDeviceAttachment (device: Device) {
const exists = this._attachedDevices.find((attachedDevice) => attachedDevice.portId === device.portId);
if (exists) { if (exists) {
// TODO NK: Remove existing zombie device this._attachedDevices.splice(this._attachedDevices.findIndex((attachedDevice) => attachedDevice.portId === device.portId), 1);
} else { } else {
this._attachedDevices.push(device); this._attachedDevices.push(device);
} }
@ -333,110 +329,126 @@ export class Hub extends EventEmitter {
} }
protected _getPortForPortNumber (num: number) { protected _detachDevice (device: Device) {
this._attachedDevices.splice(this._attachedDevices.findIndex((attachedDevice) => attachedDevice.portId === device.portId), 1);
for (const key of Object.keys(this._ports)) { /**
if (this._ports[key].value === num) { * Emits when a device is detached from the Hub.
return this._ports[key]; * @event Hub#attach
} * @param {Device} device
} */
this.emit("detach", device);
for (const key of Object.keys(this._virtualPorts)) {
if (this._virtualPorts[key].value === num) {
return this._virtualPorts[key];
}
}
return false;
} }
protected _mapSpeed (speed: number) { // Speed range of -100 to 100 is supported unless speed mapping is turned off, in which case, you're on your own! protected _getDeviceByPortId (portId: number) {
if (!this.useSpeedMap) { return this._attachedDevices.find((attachedDevice) => attachedDevice.portId === portId);
return speed;
}
if (speed === 127) {
return 127; // Hard stop
}
if (speed > 100) {
speed = 100;
} else if (speed < -100) {
speed = -100;
}
return speed;
} }
protected _calculateRamp (fromSpeed: number, toSpeed: number, time: number, port: Port) { // protected _getPortForPortNumber (num: number) {
const emitter = new EventEmitter();
const steps = Math.abs(toSpeed - fromSpeed); // for (const key of Object.keys(this._ports)) {
let delay = time / steps; // if (this._ports[key].value === num) {
let increment = 1; // return this._ports[key];
if (delay < 50 && steps > 0) { // }
increment = 50 / delay; // }
delay = 50;
} // for (const key of Object.keys(this._virtualPorts)) {
if (fromSpeed > toSpeed) { // if (this._virtualPorts[key].value === num) {
increment = -increment; // return this._virtualPorts[key];
} // }
let i = 0; // }
const interval = setInterval(() => {
let speed = Math.round(fromSpeed + (++i * increment)); // return false;
if (toSpeed > fromSpeed && speed > toSpeed) {
speed = toSpeed; // }
} else if (fromSpeed > toSpeed && speed < toSpeed) {
speed = toSpeed;
}
emitter.emit("changeSpeed", speed);
if (speed === toSpeed) {
clearInterval(interval);
emitter.emit("finished");
}
}, delay);
port.setEventTimer(interval);
return emitter;
}
protected _portLookup (portName: string) { // protected _mapSpeed (speed: number) { // Speed range of -100 to 100 is supported unless speed mapping is turned off, in which case, you're on your own!
const portNameUpper = portName.toUpperCase(); // if (!this.useSpeedMap) {
const port = this._ports[portNameUpper] || this._virtualPorts[portNameUpper]; // return speed;
if (!port) { // }
throw new Error(`Port ${portNameUpper} does not exist on this Hub type`);
}
return port;
}
private _getModeForDeviceType (type: Consts.DeviceType) { // if (speed === 127) {
switch (type) { // return 127; // Hard stop
case Consts.DeviceType.SIMPLE_MEDIUM_LINEAR_MOTOR: // }
return 0x02;
case Consts.DeviceType.TRAIN_MOTOR: // if (speed > 100) {
return 0x02; // speed = 100;
case Consts.DeviceType.BOOST_TACHO_MOTOR: // } else if (speed < -100) {
return 0x02; // speed = -100;
case Consts.DeviceType.BOOST_MOVE_HUB_MOTOR: // }
return 0x02;
case Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR: // return speed;
return 0x02; // }
case Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR:
return 0x02;
case Consts.DeviceType.CONTROL_PLUS_TILT: // protected _calculateRamp (fromSpeed: number, toSpeed: number, time: number, port: Port) {
return 0x00; // const emitter = new EventEmitter();
case Consts.DeviceType.CONTROL_PLUS_ACCELEROMETER: // const steps = Math.abs(toSpeed - fromSpeed);
return 0x00; // let delay = time / steps;
case Consts.DeviceType.COLOR_DISTANCE_SENSOR: // let increment = 1;
return (this.type === Consts.HubType.WEDO2_SMART_HUB ? 0x00 : 0x08); // if (delay < 50 && steps > 0) {
case Consts.DeviceType.BOOST_TILT: // increment = 50 / delay;
return 0x04; // delay = 50;
default: // }
return 0x00; // if (fromSpeed > toSpeed) {
} // increment = -increment;
} // }
// let i = 0;
// const interval = setInterval(() => {
// let speed = Math.round(fromSpeed + (++i * increment));
// if (toSpeed > fromSpeed && speed > toSpeed) {
// speed = toSpeed;
// } else if (fromSpeed > toSpeed && speed < toSpeed) {
// speed = toSpeed;
// }
// emitter.emit("changeSpeed", speed);
// if (speed === toSpeed) {
// clearInterval(interval);
// emitter.emit("finished");
// }
// }, delay);
// port.setEventTimer(interval);
// return emitter;
// }
// protected _portLookup (portName: string) {
// const portNameUpper = portName.toUpperCase();
// const port = this._ports[portNameUpper] || this._virtualPorts[portNameUpper];
// if (!port) {
// throw new Error(`Port ${portNameUpper} does not exist on this Hub type`);
// }
// return port;
// }
// private _getModeForDeviceType (type: Consts.DeviceType) {
// switch (type) {
// case Consts.DeviceType.SIMPLE_MEDIUM_LINEAR_MOTOR:
// return 0x02;
// case Consts.DeviceType.TRAIN_MOTOR:
// return 0x02;
// case Consts.DeviceType.BOOST_TACHO_MOTOR:
// return 0x02;
// case Consts.DeviceType.BOOST_MOVE_HUB_MOTOR:
// return 0x02;
// case Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR:
// return 0x02;
// case Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR:
// return 0x02;
// case Consts.DeviceType.CONTROL_PLUS_TILT:
// return 0x00;
// case Consts.DeviceType.CONTROL_PLUS_ACCELEROMETER:
// return 0x00;
// case Consts.DeviceType.COLOR_DISTANCE_SENSOR:
// return (this.type === Consts.HubType.WEDO2_SMART_HUB ? 0x00 : 0x08);
// case Consts.DeviceType.BOOST_TILT:
// return 0x04;
// default:
// return 0x00;
// }
// }
} }

View File

@ -48,21 +48,21 @@ export class LPF2Hub extends Hub {
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));
if (this._voltagePort !== undefined) { if (this._voltagePort !== undefined) {
this.send(Consts.BLECharacteristic.LPF2_ALL, Buffer.from([0x41, this._voltagePort, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01])); // Activate voltage reports this.send(Buffer.from([0x41, this._voltagePort, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01]), Consts.BLECharacteristic.LPF2_ALL); // Activate voltage reports
} }
if (this._currentPort !== undefined) { if (this._currentPort !== undefined) {
this.send(Consts.BLECharacteristic.LPF2_ALL, Buffer.from([0x41, this._currentPort, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01])); // Activate current reports this.send(Buffer.from([0x41, this._currentPort, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01]), Consts.BLECharacteristic.LPF2_ALL); // Activate current reports
} }
if (this.type === Consts.HubType.DUPLO_TRAIN_HUB) { if (this._type === Consts.HubType.DUPLO_TRAIN_HUB) {
this.send(Consts.BLECharacteristic.LPF2_ALL, Buffer.from([0x41, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01])); this.send(Buffer.from([0x41, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01]), Consts.BLECharacteristic.LPF2_ALL);
} }
await this.sleep(100); await this.sleep(100);
this.send(Consts.BLECharacteristic.LPF2_ALL, Buffer.from([0x01, 0x02, 0x02])); // Activate button reports this.send(Buffer.from([0x01, 0x02, 0x02]), Consts.BLECharacteristic.LPF2_ALL); // Activate button reports
this.send(Consts.BLECharacteristic.LPF2_ALL, Buffer.from([0x01, 0x03, 0x05])); // Request firmware version this.send(Buffer.from([0x01, 0x03, 0x05]), Consts.BLECharacteristic.LPF2_ALL); // Request firmware version
this.send(Consts.BLECharacteristic.LPF2_ALL, Buffer.from([0x01, 0x04, 0x05])); // Request hardware version this.send(Buffer.from([0x01, 0x04, 0x05]), Consts.BLECharacteristic.LPF2_ALL); // Request hardware version
this.send(Consts.BLECharacteristic.LPF2_ALL, Buffer.from([0x01, 0x05, 0x02])); // Activate RSSI updates this.send(Buffer.from([0x01, 0x05, 0x02]), Consts.BLECharacteristic.LPF2_ALL); // Activate RSSI updates
this.send(Consts.BLECharacteristic.LPF2_ALL, Buffer.from([0x01, 0x06, 0x02])); // Activate battery level reports this.send(Buffer.from([0x01, 0x06, 0x02]), Consts.BLECharacteristic.LPF2_ALL); // Activate battery level reports
this.send(Consts.BLECharacteristic.LPF2_ALL, Buffer.from([0x01, 0x0d, 0x05])); // Request primary MAC address this.send(Buffer.from([0x01, 0x0d, 0x05]), Consts.BLECharacteristic.LPF2_ALL); // Request primary MAC address
this.emit("connect"); this.emit("connect");
resolve(); resolve();
}); });
@ -76,7 +76,7 @@ export class LPF2Hub extends Hub {
*/ */
public shutdown () { public shutdown () {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.send(Consts.BLECharacteristic.LPF2_ALL, Buffer.from([0x02, 0x01]), () => { this.send(Buffer.from([0x02, 0x01]), Consts.BLECharacteristic.LPF2_ALL, () => {
return resolve(); return resolve();
}); });
}); });
@ -97,8 +97,8 @@ export class LPF2Hub extends Hub {
let data = Buffer.from([0x01, 0x01, 0x01]); let data = Buffer.from([0x01, 0x01, 0x01]);
data = Buffer.concat([data, Buffer.from(name, "ascii")]); data = Buffer.concat([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(Consts.BLECharacteristic.LPF2_ALL, data); this.send(data, Consts.BLECharacteristic.LPF2_ALL);
this.send(Consts.BLECharacteristic.LPF2_ALL, data); this.send(data, Consts.BLECharacteristic.LPF2_ALL);
this._name = name; this._name = name;
return resolve(); return resolve();
}); });
@ -114,12 +114,12 @@ export class LPF2Hub extends Hub {
public setLEDColor (color: number | boolean) { public setLEDColor (color: number | boolean) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let data = Buffer.from([0x41, this._ledPort, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00]); let data = Buffer.from([0x41, this._ledPort, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00]);
this.send(Consts.BLECharacteristic.LPF2_ALL, data); this.send(data, Consts.BLECharacteristic.LPF2_ALL);
if (typeof color === "boolean") { if (typeof color === "boolean") {
color = 0; color = 0;
} }
data = Buffer.from([0x81, this._ledPort, 0x11, 0x51, 0x00, color]); data = Buffer.from([0x81, this._ledPort, 0x11, 0x51, 0x00, color]);
this.send(Consts.BLECharacteristic.LPF2_ALL, data); this.send(data, Consts.BLECharacteristic.LPF2_ALL);
return resolve(); return resolve();
}); });
} }
@ -136,9 +136,9 @@ export class LPF2Hub extends Hub {
public setLEDRGB (red: number, green: number, blue: number) { public setLEDRGB (red: number, green: number, blue: number) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let data = Buffer.from([0x41, this._ledPort, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00]); let data = Buffer.from([0x41, this._ledPort, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00]);
this.send(Consts.BLECharacteristic.LPF2_ALL, data); this.send(data, Consts.BLECharacteristic.LPF2_ALL);
data = Buffer.from([0x81, this._ledPort, 0x11, 0x51, 0x01, red, green, blue]); data = Buffer.from([0x81, this._ledPort, 0x11, 0x51, 0x01, red, green, blue]);
this.send(Consts.BLECharacteristic.LPF2_ALL, data); this.send(data, Consts.BLECharacteristic.LPF2_ALL);
return resolve(); return resolve();
}); });
} }
@ -146,14 +146,14 @@ export class LPF2Hub extends Hub {
public sendRaw (message: Buffer) { public sendRaw (message: Buffer) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.send(Consts.BLECharacteristic.LPF2_ALL, message, () => { this.send(message, Consts.BLECharacteristic.LPF2_ALL, () => {
return resolve(); return resolve();
}); });
}); });
} }
public send (uuid: string, message: Buffer, callback?: () => void) { public send (message: Buffer, uuid: string, callback?: () => void) {
message = Buffer.concat([Buffer.alloc(2), message]); message = Buffer.concat([Buffer.alloc(2), message]);
message[0] = message.length; message[0] = message.length;
debug("Sent Message (LPF2_ALL)", message); debug("Sent Message (LPF2_ALL)", message);
@ -161,30 +161,30 @@ export class LPF2Hub extends Hub {
} }
protected _activatePortDevice (port: number, type: number, mode: number, format: number, callback?: () => void) { // protected _activatePortDevice (port: number, type: number, mode: number, format: number, callback?: () => void) {
this.send(Consts.BLECharacteristic.LPF2_ALL, Buffer.from([0x41, port, mode, 0x01, 0x00, 0x00, 0x00, 0x01]), callback); // this.send(Buffer.from([0x41, port, mode, 0x01, 0x00, 0x00, 0x00, 0x01]), Consts.BLECharacteristic.LPF2_ALL, callback);
} // }
protected _deactivatePortDevice (port: number, type: number, mode: number, format: number, callback?: () => void) { // protected _deactivatePortDevice (port: number, type: number, mode: number, format: number, callback?: () => void) {
this.send(Consts.BLECharacteristic.LPF2_ALL, Buffer.from([0x41, port, mode, 0x01, 0x00, 0x00, 0x00, 0x00]), callback); // this.send(Buffer.from([0x41, port, mode, 0x01, 0x00, 0x00, 0x00, 0x00]), Consts.BLECharacteristic.LPF2_ALL, callback);
} // }
protected _combinePorts (port: string, type: number) { // protected _combinePorts (port: string, type: number) {
if (!this._ports[port]) { // if (!this._ports[port]) {
return; // return;
} // }
const portObj = this._portLookup(port); // const portObj = this._portLookup(port);
if (portObj) { // if (portObj) {
Object.keys(this._ports).forEach((id) => { // Object.keys(this._ports).forEach((id) => {
if (this._ports[id].type === type && this._ports[id].value !== portObj.value && !this._virtualPorts[`${portObj.value < this._ports[id].value ? portObj.id : this._ports[id].id}${portObj.value > this._ports[id].value ? portObj.id : this._ports[id].id}`]) { // if (this._ports[id].type === type && this._ports[id].value !== portObj.value && !this._virtualPorts[`${portObj.value < this._ports[id].value ? portObj.id : this._ports[id].id}${portObj.value > this._ports[id].value ? portObj.id : this._ports[id].id}`]) {
debug("Combining ports", portObj.value < this._ports[id].value ? portObj.id : id, portObj.value > this._ports[id].value ? portObj.id : id); // debug("Combining ports", portObj.value < this._ports[id].value ? portObj.id : id, portObj.value > this._ports[id].value ? portObj.id : id);
this.send(Consts.BLECharacteristic.LPF2_ALL, Buffer.from([0x61, 0x01, portObj.value < this._ports[id].value ? portObj.value : this._ports[id].value, portObj.value > this._ports[id].value ? portObj.value : this._ports[id].value])); // this.send(Buffer.from([0x61, 0x01, portObj.value < this._ports[id].value ? portObj.value : this._ports[id].value, portObj.value > this._ports[id].value ? portObj.value : this._ports[id].value]), Consts.BLECharacteristic.LPF2_ALL);
} // }
}); // });
} // }
} // }
protected _checkFirmware (version: string) { protected _checkFirmware (version: string) {
@ -297,6 +297,7 @@ export class LPF2Hub extends Hub {
const event = data[4]; const event = data[4];
const deviceType = event ? data.readUInt16LE(5) : 0; const deviceType = event ? data.readUInt16LE(5) : 0;
// Handle device attachments
if (event === 0x01) { if (event === 0x01) {
let device; let device;
@ -313,8 +314,14 @@ export class LPF2Hub extends Hub {
break; break;
} }
this._registerDeviceAttachment(device); this._attachDevice(device);
// Handle device detachments
} else if (event === 0x00) {
const device = this._getDeviceByPortId(portId);
if (device) {
this._detachDevice(device);
}
} }
@ -358,8 +365,8 @@ export class LPF2Hub extends Hub {
private _sendPortInformationRequest (port: number) { private _sendPortInformationRequest (port: number) {
this.send(Consts.BLECharacteristic.LPF2_ALL, Buffer.from([0x21, port, 0x01])); this.send(Buffer.from([0x21, port, 0x01]), Consts.BLECharacteristic.LPF2_ALL);
this.send(Consts.BLECharacteristic.LPF2_ALL, Buffer.from([0x21, port, 0x02])); // Mode combinations this.send(Buffer.from([0x21, port, 0x02]), Consts.BLECharacteristic.LPF2_ALL); // Mode combinations
} }
@ -390,7 +397,7 @@ export class LPF2Hub extends Hub {
private _sendModeInformationRequest (port: number, mode: number, type: number) { private _sendModeInformationRequest (port: number, mode: number, type: number) {
this.send(Consts.BLECharacteristic.LPF2_ALL, Buffer.from([0x22, port, mode, type])); this.send(Buffer.from([0x22, port, mode, type]), Consts.BLECharacteristic.LPF2_ALL);
} }
@ -426,233 +433,233 @@ export class LPF2Hub extends Hub {
private _parsePortAction (data: Buffer) { private _parsePortAction (data: Buffer) {
const port = this._getPortForPortNumber(data[3]); // const port = this._getPortForPortNumber(data[3]);
if (!port) { // if (!port) {
return; // return;
} // }
if (data[4] === 0x0a) { // if (data[4] === 0x0a) {
port.busy = false; // port.busy = false;
if (port.finished) { // if (port.finished) {
port.finished(); // port.finished();
port.finished = null; // port.finished = null;
} // }
} // }
} }
private _parseSensorMessage (data: Buffer) { private _parseSensorMessage (data: Buffer) {
if (data[3] === this._voltagePort) { // if (data[3] === this._voltagePort) {
const voltageRaw = data.readUInt16LE(4); // const voltageRaw = data.readUInt16LE(4);
this._voltage = voltageRaw * this._voltageMaxV / this._voltageMaxRaw; // this._voltage = voltageRaw * this._voltageMaxV / this._voltageMaxRaw;
return; // return;
} else if (data[3] === this._currentPort) { // } else if (data[3] === this._currentPort) {
const currentRaw = data.readUInt16LE(4); // const currentRaw = data.readUInt16LE(4);
this._current = this._currentMaxMA * currentRaw / this._currentMaxRaw; // this._current = this._currentMaxMA * currentRaw / this._currentMaxRaw;
return; // return;
} // }
if ((data[3] === 0x3d && this.type === Consts.HubType.CONTROL_PLUS_HUB)) { // Control+ CPU Temperature // if ((data[3] === 0x3d && this.type === Consts.HubType.CONTROL_PLUS_HUB)) { // Control+ CPU Temperature
/** // /**
* Emits when a change is detected on a temperature sensor. Measured in degrees centigrade. // * Emits when a change is detected on a temperature sensor. Measured in degrees centigrade.
* @event LPF2Hub#temp // * @event LPF2Hub#temp
* @param {string} port For Control+ Hubs, port will be "CPU" as the sensor reports CPU temperature. // * @param {string} port For Control+ Hubs, port will be "CPU" as the sensor reports CPU temperature.
* @param {number} temp // * @param {number} temp
*/ // */
this.emit("temp", "CPU", ((data.readInt16LE(4) / 900) * 90).toFixed(2)); // this.emit("temp", "CPU", ((data.readInt16LE(4) / 900) * 90).toFixed(2));
return; // return;
} // }
const port = this._getPortForPortNumber(data[3]); // const port = this._getPortForPortNumber(data[3]);
if (!port) { // if (!port) {
return; // return;
} // }
if (port && port.connected) { // if (port && port.connected) {
switch (port.type) { // switch (port.type) {
case Consts.DeviceType.WEDO2_DISTANCE: { // case Consts.DeviceType.WEDO2_DISTANCE: {
let distance = data[4]; // let distance = data[4];
if (data[5] === 1) { // if (data[5] === 1) {
distance = data[4] + 255; // distance = data[4] + 255;
} // }
/** // /**
* Emits when a distance sensor is activated. // * Emits when a distance sensor is activated.
* @event LPF2Hub#distance // * @event LPF2Hub#distance
* @param {string} port // * @param {string} port
* @param {number} distance Distance, in millimeters. // * @param {number} distance Distance, in millimeters.
*/ // */
this.emit("distance", port.id, distance * 10); // this.emit("distance", port.id, distance * 10);
break; // break;
} // }
case Consts.DeviceType.COLOR_DISTANCE_SENSOR: { // case Consts.DeviceType.COLOR_DISTANCE_SENSOR: {
/** // /**
* Emits when a color sensor is activated. // * Emits when a color sensor is activated.
* @event LPF2Hub#color // * @event LPF2Hub#color
* @param {string} port // * @param {string} port
* @param {Color} color // * @param {Color} color
*/ // */
if (data[4] <= 10) { // if (data[4] <= 10) {
this.emit("color", port.id, data[4]); // this.emit("color", port.id, data[4]);
} // }
let distance = data[5]; // let distance = data[5];
const partial = data[7]; // const partial = data[7];
if (partial > 0) { // if (partial > 0) {
distance += 1.0 / partial; // distance += 1.0 / partial;
} // }
distance = Math.floor(distance * 25.4) - 20; // distance = Math.floor(distance * 25.4) - 20;
this.emit("distance", port.id, distance); // this.emit("distance", port.id, distance);
/** // /**
* A combined color and distance event, emits when the sensor is activated. // * A combined color and distance event, emits when the sensor is activated.
* @event LPF2Hub#colorAndDistance // * @event LPF2Hub#colorAndDistance
* @param {string} port // * @param {string} port
* @param {Color} color // * @param {Color} color
* @param {number} distance Distance, in millimeters. // * @param {number} distance Distance, in millimeters.
*/ // */
if (data[4] <= 10) { // if (data[4] <= 10) {
this.emit("colorAndDistance", port.id, data[4], distance); // this.emit("colorAndDistance", port.id, data[4], distance);
} // }
break; // break;
} // }
case Consts.DeviceType.WEDO2_TILT: { // case Consts.DeviceType.WEDO2_TILT: {
const tiltX = data.readInt8(4); // const tiltX = data.readInt8(4);
const tiltY = data.readInt8(5); // const tiltY = data.readInt8(5);
this._lastTiltX = tiltX; // this._lastTiltX = tiltX;
this._lastTiltY = tiltY; // this._lastTiltY = tiltY;
/** // /**
* Emits when a tilt sensor is activated. // * Emits when a tilt sensor is activated.
* @event LPF2Hub#tilt // * @event LPF2Hub#tilt
* @param {string} port If the event is fired from the Move Hub or Control+ Hub's in-built tilt sensor, the special port "TILT" is used. // * @param {string} port If the event is fired from the Move Hub or Control+ Hub's in-built tilt sensor, the special port "TILT" is used.
* @param {number} x // * @param {number} x
* @param {number} y // * @param {number} y
* @param {number} z (Only available when using a Control+ Hub) // * @param {number} z (Only available when using a Control+ Hub)
*/ // */
this.emit("tilt", port.id, this._lastTiltX, this._lastTiltY, this._lastTiltZ); // this.emit("tilt", port.id, this._lastTiltX, this._lastTiltY, this._lastTiltZ);
break; // break;
} // }
case Consts.DeviceType.BOOST_TACHO_MOTOR: { // case Consts.DeviceType.BOOST_TACHO_MOTOR: {
const rotation = data.readInt32LE(4); // const rotation = data.readInt32LE(4);
/** // /**
* Emits when a rotation sensor is activated. // * Emits when a rotation sensor is activated.
* @event LPF2Hub#rotate // * @event LPF2Hub#rotate
* @param {string} port // * @param {string} port
* @param {number} rotation // * @param {number} rotation
*/ // */
this.emit("rotate", port.id, rotation); // this.emit("rotate", port.id, rotation);
break; // break;
} // }
case Consts.DeviceType.BOOST_MOVE_HUB_MOTOR: { // case Consts.DeviceType.BOOST_MOVE_HUB_MOTOR: {
const rotation = data.readInt32LE(4); // const rotation = data.readInt32LE(4);
this.emit("rotate", port.id, rotation); // this.emit("rotate", port.id, rotation);
break; // break;
} // }
case Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR: { // case Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR: {
const rotation = data.readInt32LE(4); // const rotation = data.readInt32LE(4);
this.emit("rotate", port.id, rotation); // this.emit("rotate", port.id, rotation);
break; // break;
} // }
case Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR: { // case Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR: {
const rotation = data.readInt32LE(4); // const rotation = data.readInt32LE(4);
this.emit("rotate", port.id, rotation); // this.emit("rotate", port.id, rotation);
break; // break;
} // }
case Consts.DeviceType.CONTROL_PLUS_TILT: { // case Consts.DeviceType.CONTROL_PLUS_TILT: {
const tiltZ = data.readInt16LE(4); // const tiltZ = data.readInt16LE(4);
const tiltY = data.readInt16LE(6); // const tiltY = data.readInt16LE(6);
const tiltX = data.readInt16LE(8); // const tiltX = data.readInt16LE(8);
this._lastTiltX = tiltX; // this._lastTiltX = tiltX;
this._lastTiltY = tiltY; // this._lastTiltY = tiltY;
this._lastTiltZ = tiltZ; // this._lastTiltZ = tiltZ;
this.emit("tilt", "TILT", this._lastTiltX, this._lastTiltY, this._lastTiltZ); // this.emit("tilt", "TILT", this._lastTiltX, this._lastTiltY, this._lastTiltZ);
break; // break;
} // }
case Consts.DeviceType.CONTROL_PLUS_GYRO: { // case Consts.DeviceType.CONTROL_PLUS_GYRO: {
const gyroX = Math.round(data.readInt16LE(4) * 7 / 400); // const gyroX = Math.round(data.readInt16LE(4) * 7 / 400);
const gyroY = Math.round(data.readInt16LE(6) * 7 / 400); // const gyroY = Math.round(data.readInt16LE(6) * 7 / 400);
const gyroZ = Math.round(data.readInt16LE(8) * 7 / 400); // const gyroZ = Math.round(data.readInt16LE(8) * 7 / 400);
/** // /**
* Emits when gyroscope detects movement. Measured in DPS - degrees per second. // * Emits when gyroscope detects movement. Measured in DPS - degrees per second.
* @event LPF2Hub#gyro // * @event LPF2Hub#gyro
* @param {string} port // * @param {string} port
* @param {number} x // * @param {number} x
* @param {number} y // * @param {number} y
* @param {number} z // * @param {number} z
*/ // */
this.emit("gyro", "GYRO", gyroX, gyroY, gyroZ); // this.emit("gyro", "GYRO", gyroX, gyroY, gyroZ);
break; // break;
} // }
case Consts.DeviceType.CONTROL_PLUS_ACCELEROMETER: { // case Consts.DeviceType.CONTROL_PLUS_ACCELEROMETER: {
const accelX = Math.round(data.readInt16LE(4) / 4.096); // const accelX = Math.round(data.readInt16LE(4) / 4.096);
const accelY = Math.round(data.readInt16LE(6) / 4.096); // const accelY = Math.round(data.readInt16LE(6) / 4.096);
const accelZ = Math.round(data.readInt16LE(8) / 4.096); // const accelZ = Math.round(data.readInt16LE(8) / 4.096);
/** // /**
* Emits when accelerometer detects movement. Measured in mG. // * Emits when accelerometer detects movement. Measured in mG.
* @event LPF2Hub#accel // * @event LPF2Hub#accel
* @param {string} port // * @param {string} port
* @param {number} x // * @param {number} x
* @param {number} y // * @param {number} y
* @param {number} z // * @param {number} z
*/ // */
this.emit("accel", "ACCEL", accelX, accelY, accelZ); // this.emit("accel", "ACCEL", accelX, accelY, accelZ);
break; // break;
} // }
case Consts.DeviceType.BOOST_TILT: { // case Consts.DeviceType.BOOST_TILT: {
const tiltX = data.readInt8(4); // const tiltX = data.readInt8(4);
const tiltY = data.readInt8(5); // const tiltY = data.readInt8(5);
this._lastTiltX = tiltX; // this._lastTiltX = tiltX;
this._lastTiltY = tiltY; // this._lastTiltY = tiltY;
this.emit("tilt", port.id, this._lastTiltX, this._lastTiltY, this._lastTiltZ); // this.emit("tilt", port.id, this._lastTiltX, this._lastTiltY, this._lastTiltZ);
break; // break;
} // }
case Consts.DeviceType.POWERED_UP_REMOTE_BUTTON: { // case Consts.DeviceType.POWERED_UP_REMOTE_BUTTON: {
switch (data[4]) { // switch (data[4]) {
case 0x01: { // case 0x01: {
this.emit("button", port.id, Consts.ButtonState.UP); // this.emit("button", port.id, Consts.ButtonState.UP);
break; // break;
} // }
case 0xff: { // case 0xff: {
this.emit("button", port.id, Consts.ButtonState.DOWN); // this.emit("button", port.id, Consts.ButtonState.DOWN);
break; // break;
} // }
case 0x7f: { // case 0x7f: {
this.emit("button", port.id, Consts.ButtonState.STOP); // this.emit("button", port.id, Consts.ButtonState.STOP);
break; // break;
} // }
case 0x00: { // case 0x00: {
this.emit("button", port.id, Consts.ButtonState.RELEASED); // this.emit("button", port.id, Consts.ButtonState.RELEASED);
break; // break;
} // }
} // }
break; // break;
} // }
case Consts.DeviceType.DUPLO_TRAIN_BASE_COLOR: { // case Consts.DeviceType.DUPLO_TRAIN_BASE_COLOR: {
if (data[4] <= 10) { // if (data[4] <= 10) {
this.emit("color", port.id, data[4]); // this.emit("color", port.id, data[4]);
} // }
break; // break;
} // }
case Consts.DeviceType.DUPLO_TRAIN_BASE_SPEEDOMETER: { // case Consts.DeviceType.DUPLO_TRAIN_BASE_SPEEDOMETER: {
/** // /**
* Emits on a speed change. // * Emits on a speed change.
* @event LPF2Hub#speed // * @event LPF2Hub#speed
* @param {string} port // * @param {string} port
* @param {number} speed // * @param {number} speed
*/ // */
const speed = data.readInt16LE(4); // const speed = data.readInt16LE(4);
this.emit("speed", port.id, speed); // this.emit("speed", port.id, speed);
break; // break;
} // }
} // }
} // }
} }

View File

@ -36,14 +36,11 @@ export class PUPHub extends LPF2Hub {
constructor (device: IBLEAbstraction, autoSubscribe: boolean = true) { constructor (device: IBLEAbstraction, autoSubscribe: boolean = true) {
super(device, autoSubscribe); super(device, autoSubscribe);
this.type = Consts.HubType.POWERED_UP_HUB; this._type = Consts.HubType.POWERED_UP_HUB;
this._ports = { this._portNames = {
"A": new Port("A", 0), "A": 0,
"B": new Port("B", 1) "B": 1
}; };
this.on("attach", (port, type) => {
this._combinePorts(port, type);
});
debug("Discovered Powered UP Hub"); debug("Discovered Powered UP Hub");
} }
@ -58,239 +55,6 @@ export class PUPHub extends LPF2Hub {
} }
/**
* Set the motor speed on a given port.
* @method PUPHub#setMotorSpeed
* @param {string} port
* @param {number | Array.<number>} speed For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0. If you are specifying port AB to control both motors, you can optionally supply a tuple of speeds.
* @param {number} [time] How long to activate the motor for (in milliseconds). Leave empty to turn the motor on indefinitely.
* @returns {Promise} Resolved upon successful completion of command. If time is specified, this is once the motor is finished.
*/
public setMotorSpeed (port: string, speed: number | [number, number], time?: number | boolean) {
const portObj = this._portLookup(port);
if (!this._virtualPorts[portObj.id] && speed instanceof Array) {
throw new Error(`Port ${portObj.id} can only accept a single speed`);
}
let cancelEventTimer = true;
if (typeof time === "boolean") {
if (time === true) {
cancelEventTimer = false;
}
time = undefined;
}
if (cancelEventTimer) {
portObj.cancelEventTimer();
}
return new Promise((resolve, reject) => {
if (time && typeof time === "number") {
if (
portObj.type === Consts.DeviceType.BOOST_TACHO_MOTOR ||
portObj.type === Consts.DeviceType.BOOST_MOVE_HUB_MOTOR ||
portObj.type === Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR ||
portObj.type === Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR
) {
portObj.busy = true;
let data = null;
if (this._virtualPorts[portObj.id]) {
data = Buffer.from([0x81, portObj.value, 0x11, 0x0a, 0x00, 0x00, this._mapSpeed(speed instanceof Array ? speed[0] : speed), this._mapSpeed(speed instanceof Array ? speed[1] : speed), 0x64, 0x7f, 0x03]);
} else {
// @ts-ignore: The type of speed is properly checked at the start
data = Buffer.from([0x81, portObj.value, 0x11, 0x09, 0x00, 0x00, this._mapSpeed(speed), 0x64, 0x7f, 0x03]);
}
data.writeUInt16LE(time > 65535 ? 65535 : time, 4);
this.send(Consts.BLECharacteristic.LPF2_ALL, data);
portObj.finished = () => {
return resolve();
};
} else {
// @ts-ignore: The type of speed is properly checked at the start
const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, this._mapSpeed(speed)]);
this.send(Consts.BLECharacteristic.LPF2_ALL, data);
const timeout = global.setTimeout(() => {
const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, 0x00]);
this.send(Consts.BLECharacteristic.LPF2_ALL, data);
return resolve();
// @ts-ignore: The type of time is properly checked at the start
}, time);
portObj.setEventTimer(timeout);
}
} else {
if (portObj.type === Consts.DeviceType.BOOST_TACHO_MOTOR || portObj.type === Consts.DeviceType.BOOST_MOVE_HUB_MOTOR) {
portObj.busy = true;
let data = null;
if (this._virtualPorts[portObj.id]) {
data = Buffer.from([0x81, portObj.value, 0x11, 0x02, this._mapSpeed(speed instanceof Array ? speed[0] : speed), this._mapSpeed(speed instanceof Array ? speed[1] : speed), 0x64, 0x7f, 0x03]);
} else {
// @ts-ignore: The type of speed is properly checked at the start
data = Buffer.from([0x81, portObj.value, 0x11, 0x01, this._mapSpeed(speed), 0x64, 0x7f, 0x03]);
}
this.send(Consts.BLECharacteristic.LPF2_ALL, data);
portObj.finished = () => {
return resolve();
};
} else {
// @ts-ignore: The type of speed is properly checked at the start
const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, this._mapSpeed(speed)]);
this.send(Consts.BLECharacteristic.LPF2_ALL, data);
}
}
});
}
/**
* Ramp the motor speed on a given port.
* @method PUPHub#rampMotorSpeed
* @param {string} port
* @param {number} fromSpeed For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0.
* @param {number} toSpeed For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0.
* @param {number} time How long the ramp should last (in milliseconds).
* @returns {Promise} Resolved upon successful completion of command.
*/
public rampMotorSpeed (port: string, fromSpeed: number, toSpeed: number, time: number) {
const portObj = this._portLookup(port);
portObj.cancelEventTimer();
return new Promise((resolve, reject) => {
this._calculateRamp(fromSpeed, toSpeed, time, portObj)
.on("changeSpeed", (speed) => {
this.setMotorSpeed(port, speed, true);
})
.on("finished", resolve);
});
}
/**
* Rotate a motor by a given angle.
* @method PUPHub#setMotorAngle
* @param {string} port
* @param {number} angle How much the motor should be rotated (in degrees).
* @param {number | Array.<number>} [speed=100] For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0. If you are specifying port AB to control both motors, you can optionally supply a tuple of speeds.
* @returns {Promise} Resolved upon successful completion of command (ie. once the motor is finished).
*/
public setMotorAngle (port: string, angle: number, speed: number | [number, number] = 100) {
const portObj = this._portLookup(port);
if (!(
portObj.type === Consts.DeviceType.BOOST_TACHO_MOTOR ||
portObj.type === Consts.DeviceType.BOOST_MOVE_HUB_MOTOR ||
portObj.type === Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR ||
portObj.type === Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR
)) {
throw new Error("Angle rotation is only available when using a Boost Tacho Motor, Boost Move Hub Motor, Control+ Medium Motor, or Control+ Large Motor");
}
if (!this._virtualPorts[portObj.id] && speed instanceof Array) {
throw new Error(`Port ${portObj.id} can only accept a single speed`);
}
portObj.cancelEventTimer();
return new Promise((resolve, reject) => {
portObj.busy = true;
let data = null;
if (this._virtualPorts[portObj.id]) {
data = Buffer.from([0x81, portObj.value, 0x11, 0x0c, 0x00, 0x00, 0x00, 0x00, this._mapSpeed(speed instanceof Array ? speed[0] : speed), this._mapSpeed(speed instanceof Array ? speed[1] : speed), 0x64, 0x7f, 0x03]);
} else {
// @ts-ignore: The type of speed is properly checked at the start
data = Buffer.from([0x81, portObj.value, 0x11, 0x0b, 0x00, 0x00, 0x00, 0x00, this._mapSpeed(speed), 0x64, 0x7f, 0x03]);
}
data.writeUInt32LE(angle, 4);
this.send(Consts.BLECharacteristic.LPF2_ALL, data);
portObj.finished = () => {
return resolve();
};
});
}
/**
* Tell motor to goto an absolute position
* @method PUPHub#setAbsolutePosition
* @param {string} port
* @param {number} pos The position of the motor to go to
* @param {number | Array.<number>} [speed=100] A value between 1 - 100 should be set (Direction does not apply when going to absolute position)
* @returns {Promise} Resolved upon successful completion of command (ie. once the motor is finished).
*/
public setAbsolutePosition (port: string, pos: number, speed: number = 100) {
const portObj = this._portLookup(port);
if (!(
portObj.type === Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR ||
portObj.type === Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR
)) {
throw new Error("Absolute positioning is only available when using a Control+ Medium Motor, or Control+ Large Motor");
}
portObj.cancelEventTimer();
return new Promise((resolve, reject) => {
portObj.busy = true;
let data = null;
if (this._virtualPorts[portObj.id]) {
data = Buffer.from([0x81, portObj.value, 0x11, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, this._mapSpeed(speed), 0x64, 0x7f, 0x03]);
data.writeInt32LE(pos, 4);
data.writeInt32LE(pos, 8);
} else {
// @ts-ignore: The type of speed is properly checked at the start
data = Buffer.from([0x81, portObj.value, 0x11, 0x0d, 0x00, 0x00, 0x00, 0x00, this._mapSpeed(speed), 0x64, 0x7f, 0x03]);
data.writeInt32LE(pos, 4);
}
this.send(Consts.BLECharacteristic.LPF2_ALL, data);
portObj.finished = () => {
return resolve();
};
});
}
/**
* Reset the current motor position as absolute position zero
* @method PUPHub#resetAbsolutePosition
* @param {string} port
* @returns {Promise} Resolved upon successful completion of command (ie. once the motor is finished).
*/
public resetAbsolutePosition (port: string) {
const portObj = this._portLookup(port);
if (!(
portObj.type === Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR ||
portObj.type === Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR
)) {
throw new Error("Absolute positioning is only available when using a Control+ Medium Motor, or Control+ Large Motor");
}
return new Promise((resolve) => {
const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x02, 0x00, 0x00, 0x00, 0x00]);
this.send(Consts.BLECharacteristic.LPF2_ALL, data);
return resolve();
});
}
/**
* Set the light brightness on a given port.
* @method PUPHub#setLightBrightness
* @param {string} port
* @param {number} brightness Brightness value between 0-100 (0 is off)
* @param {number} [time] How long to turn the light on (in milliseconds). Leave empty to turn the light on indefinitely.
* @returns {Promise} Resolved upon successful completion of command. If time is specified, this is once the light is turned off.
*/
public setLightBrightness (port: string, brightness: number, time?: number) {
const portObj = this._portLookup(port);
portObj.cancelEventTimer();
return new Promise((resolve, reject) => {
const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, brightness]);
this.send(Consts.BLECharacteristic.LPF2_ALL, data);
if (time) {
const timeout = global.setTimeout(() => {
const data = Buffer.from([0x81, portObj.value, 0x11, 0x51, 0x00, 0x00]);
this.send(Consts.BLECharacteristic.LPF2_ALL, data);
return resolve();
}, time);
portObj.setEventTimer(timeout);
} else {
return resolve();
}
});
}
protected _checkFirmware (version: string) { protected _checkFirmware (version: string) {
if (compareVersion("1.1.00.0004", version) === 1) { if (compareVersion("1.1.00.0004", version) === 1) {
throw new Error(`Your Powered Up Hub's (${this.name}) firmware is out of date and unsupported by this library. Please update it via the official Powered Up app.`); throw new Error(`Your Powered Up Hub's (${this.name}) firmware is out of date and unsupported by this library. Please update it via the official Powered Up app.`);

View File

@ -39,10 +39,10 @@ export class PUPRemote extends LPF2Hub {
constructor (device: IBLEAbstraction, autoSubscribe: boolean = true) { constructor (device: IBLEAbstraction, autoSubscribe: boolean = true) {
super(device, autoSubscribe); super(device, autoSubscribe);
this.type = Consts.HubType.POWERED_UP_REMOTE; this._type = Consts.HubType.POWERED_UP_REMOTE;
this._ports = { this._portNames = {
"LEFT": new Port("LEFT", 0), "LEFT": 0,
"RIGHT": new Port("RIGHT", 1) "RIGHT": 1
}; };
debug("Discovered Powered UP Remote"); debug("Discovered Powered UP Remote");
} }

View File

@ -1,9 +1,22 @@
// @ts-ignore // @ts-ignore
export const isWebBluetooth = (typeof navigator !== "undefined" && navigator && navigator.bluetooth); export const isWebBluetooth = (typeof navigator !== "undefined" && navigator && navigator.bluetooth);
export function toHex (value: number, length: number = 2) { export const toHex = (value: number, length: number = 2) => {
return value.toString(16).padStart(length, "0"); return value.toString(16).padStart(length, "0");
} };
export function toBin (value: number, length: number = 8) {
export const toBin = (value: number, length: number = 8) => {
return value.toString(2).padStart(length, "0"); return value.toString(2).padStart(length, "0");
};
export const mapSpeed = (speed: number) => {
if (speed === 127) {
return 127;
} }
if (speed > 100) {
speed = 100;
} else if (speed < -100) {
speed = -100;
}
return speed;
};

View File

@ -38,10 +38,10 @@ export class WeDo2SmartHub extends Hub {
constructor (device: IBLEAbstraction, autoSubscribe: boolean = true) { constructor (device: IBLEAbstraction, autoSubscribe: boolean = true) {
super(device, autoSubscribe); super(device, autoSubscribe);
this.type = Consts.HubType.WEDO2_SMART_HUB; this._type = Consts.HubType.WEDO2_SMART_HUB;
this._ports = { this._portNames = {
"A": new Port("A", 1), "A": 1,
"B": new Port("B", 2) "B": 2
}; };
debug("Discovered WeDo 2.0 Smart Hub"); debug("Discovered WeDo 2.0 Smart Hub");
} }
@ -115,8 +115,8 @@ export class WeDo2SmartHub extends Hub {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
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(Consts.BLECharacteristic.WEDO2_NAME_ID, data); this.send(data, Consts.BLECharacteristic.WEDO2_NAME_ID);
this.send(Consts.BLECharacteristic.WEDO2_NAME_ID, data); this.send(data, Consts.BLECharacteristic.WEDO2_NAME_ID);
this._name = name; this._name = name;
return resolve(); return resolve();
}); });
@ -132,12 +132,12 @@ export class WeDo2SmartHub extends Hub {
public setLEDColor (color: number | boolean) { public setLEDColor (color: number | boolean) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let data = Buffer.from([0x06, 0x17, 0x01, 0x01]); let data = Buffer.from([0x06, 0x17, 0x01, 0x01]);
this.send(Consts.BLECharacteristic.WEDO2_PORT_TYPE_WRITE, data); this.send(data, Consts.BLECharacteristic.WEDO2_PORT_TYPE_WRITE);
if (typeof color === "boolean") { if (typeof color === "boolean") {
color = 0; color = 0;
} }
data = Buffer.from([0x06, 0x04, 0x01, color]); data = Buffer.from([0x06, 0x04, 0x01, color]);
this.send(Consts.BLECharacteristic.WEDO2_MOTOR_VALUE_WRITE, data); this.send(data, Consts.BLECharacteristic.WEDO2_MOTOR_VALUE_WRITE);
return resolve(); return resolve();
}); });
} }
@ -150,7 +150,7 @@ export class WeDo2SmartHub extends Hub {
*/ */
public shutdown () { public shutdown () {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.send(Consts.BLECharacteristic.WEDO2_DISCONNECT, Buffer.from([0x00]), () => { this.send(Buffer.from([0x00]), Consts.BLECharacteristic.WEDO2_DISCONNECT, () => {
return resolve(); return resolve();
}); });
}); });
@ -168,80 +168,80 @@ export class WeDo2SmartHub extends Hub {
public setLEDRGB (red: number, green: number, blue: number) { public setLEDRGB (red: number, green: number, blue: number) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let data = Buffer.from([0x06, 0x17, 0x01, 0x02]); let data = Buffer.from([0x06, 0x17, 0x01, 0x02]);
this.send(Consts.BLECharacteristic.WEDO2_PORT_TYPE_WRITE, data); this.send(data, Consts.BLECharacteristic.WEDO2_PORT_TYPE_WRITE);
data = Buffer.from([0x06, 0x04, 0x03, red, green, blue]); data = Buffer.from([0x06, 0x04, 0x03, red, green, blue]);
this.send(Consts.BLECharacteristic.WEDO2_MOTOR_VALUE_WRITE, data); this.send(data, Consts.BLECharacteristic.WEDO2_MOTOR_VALUE_WRITE);
return resolve(); return resolve();
}); });
} }
/** // /**
* Set the motor speed on a given port. // * Set the motor speed on a given port.
* @method WeDo2SmartHub#setMotorSpeed // * @method WeDo2SmartHub#setMotorSpeed
* @param {string} port // * @param {string} port
* @param {number} speed For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0. // * @param {number} speed For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0.
* @param {number} [time] How long to activate the motor for (in milliseconds). Leave empty to turn the motor on indefinitely. // * @param {number} [time] How long to activate the motor for (in milliseconds). Leave empty to turn the motor on indefinitely.
* @returns {Promise} Resolved upon successful completion of command. If time is specified, this is once the motor is finished. // * @returns {Promise} Resolved upon successful completion of command. If time is specified, this is once the motor is finished.
*/ // */
public setMotorSpeed (port: string, speed: number, time?: number | boolean) { // public setMotorSpeed (port: string, speed: number, time?: number | boolean) {
const portObj = this._portLookup(port); // const portObj = this._portLookup(port);
let cancelEventTimer = true; // let cancelEventTimer = true;
if (typeof time === "boolean") { // if (typeof time === "boolean") {
if (time === true) { // if (time === true) {
cancelEventTimer = false; // cancelEventTimer = false;
} // }
time = undefined; // time = undefined;
} // }
if (cancelEventTimer) { // if (cancelEventTimer) {
portObj.cancelEventTimer(); // portObj.cancelEventTimer();
} // }
return new Promise((resolve, reject) => { // return new Promise((resolve, reject) => {
this.send(Consts.BLECharacteristic.WEDO2_MOTOR_VALUE_WRITE, Buffer.from([portObj.value, 0x01, 0x02, this._mapSpeed(speed)])); // this.send(Buffer.from([portObj.value, 0x01, 0x02, this._mapSpeed(speed)]), Consts.BLECharacteristic.WEDO2_MOTOR_VALUE_WRITE);
if (time && typeof time === "number") { // if (time && typeof time === "number") {
const timeout = global.setTimeout(() => { // const timeout = global.setTimeout(() => {
this.send(Consts.BLECharacteristic.WEDO2_MOTOR_VALUE_WRITE, Buffer.from([portObj.value, 0x01, 0x02, 0x00])); // this.send(Buffer.from([portObj.value, 0x01, 0x02, 0x00]), Consts.BLECharacteristic.WEDO2_MOTOR_VALUE_WRITE);
return resolve(); // return resolve();
}, time); // }, time);
portObj.setEventTimer(timeout); // portObj.setEventTimer(timeout);
} else { // } else {
return resolve(); // return resolve();
} // }
}); // });
} // }
/** // /**
* Ramp the motor speed on a given port. // * Ramp the motor speed on a given port.
* @method WeDo2SmartHub#rampMotorSpeed // * @method WeDo2SmartHub#rampMotorSpeed
* @param {string} port // * @param {string} port
* @param {number} fromSpeed For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0. // * @param {number} fromSpeed For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0.
* @param {number} toSpeed For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0. // * @param {number} toSpeed For forward, a value between 1 - 100 should be set. For reverse, a value between -1 to -100. Stop is 0.
* @param {number} time How long the ramp should last (in milliseconds). // * @param {number} time How long the ramp should last (in milliseconds).
* @returns {Promise} Resolved upon successful completion of command. // * @returns {Promise} Resolved upon successful completion of command.
*/ // */
public rampMotorSpeed (port: string, fromSpeed: number, toSpeed: number, time: number) { // public rampMotorSpeed (port: string, fromSpeed: number, toSpeed: number, time: number) {
const portObj = this._portLookup(port); // const portObj = this._portLookup(port);
portObj.cancelEventTimer(); // portObj.cancelEventTimer();
return new Promise((resolve, reject) => { // return new Promise((resolve, reject) => {
this._calculateRamp(fromSpeed, toSpeed, time, portObj) // this._calculateRamp(fromSpeed, toSpeed, time, portObj)
.on("changeSpeed", (speed) => { // .on("changeSpeed", (speed) => {
this.setMotorSpeed(port, speed, true); // this.setMotorSpeed(port, speed, true);
}) // })
.on("finished", resolve); // .on("finished", resolve);
}); // });
} // }
/** // /**
* Fully (hard) stop the motor on a given port. // * Fully (hard) stop the motor on a given port.
* @method WeDo2SmartHub#brakeMotor // * @method WeDo2SmartHub#brakeMotor
* @param {string} port // * @param {string} port
* @returns {Promise} Resolved upon successful completion of command. // * @returns {Promise} Resolved upon successful completion of command.
*/ // */
public brakeMotor (port: string) { // public brakeMotor (port: string) {
return this.setMotorSpeed(port, 127); // return this.setMotorSpeed(port, 127);
} // }
/** /**
@ -256,41 +256,41 @@ export class WeDo2SmartHub extends Hub {
const data = Buffer.from([0x05, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00]); const data = Buffer.from([0x05, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00]);
data.writeUInt16LE(frequency, 3); data.writeUInt16LE(frequency, 3);
data.writeUInt16LE(time, 5); data.writeUInt16LE(time, 5);
this.send(Consts.BLECharacteristic.WEDO2_MOTOR_VALUE_WRITE, data); this.send(data, Consts.BLECharacteristic.WEDO2_MOTOR_VALUE_WRITE);
global.setTimeout(resolve, time); global.setTimeout(resolve, time);
}); });
} }
/** // /**
* Set the light brightness on a given port. // * Set the light brightness on a given port.
* @method WeDo2SmartHub#setLightBrightness // * @method WeDo2SmartHub#setLightBrightness
* @param {string} port // * @param {string} port
* @param {number} brightness Brightness value between 0-100 (0 is off) // * @param {number} brightness Brightness value between 0-100 (0 is off)
* @param {number} [time] How long to turn the light on (in milliseconds). Leave empty to turn the light on indefinitely. // * @param {number} [time] How long to turn the light on (in milliseconds). Leave empty to turn the light on indefinitely.
* @returns {Promise} Resolved upon successful completion of command. If time is specified, this is once the light is turned off. // * @returns {Promise} Resolved upon successful completion of command. If time is specified, this is once the light is turned off.
*/ // */
public setLightBrightness (port: string, brightness: number, time?: number) { // public setLightBrightness (port: string, brightness: number, time?: number) {
const portObj = this._portLookup(port); // const portObj = this._portLookup(port);
portObj.cancelEventTimer(); // portObj.cancelEventTimer();
return new Promise((resolve, reject) => { // return new Promise((resolve, reject) => {
const data = Buffer.from([portObj.value, 0x01, 0x02, brightness]); // const data = Buffer.from([portObj.value, 0x01, 0x02, brightness]);
this.send(Consts.BLECharacteristic.WEDO2_MOTOR_VALUE_WRITE, data); // this.send(data, Consts.BLECharacteristic.WEDO2_MOTOR_VALUE_WRITE);
if (time) { // if (time) {
const timeout = global.setTimeout(() => { // const timeout = global.setTimeout(() => {
const data = Buffer.from([portObj.value, 0x01, 0x02, 0x00]); // const data = Buffer.from([portObj.value, 0x01, 0x02, 0x00]);
this.send(Consts.BLECharacteristic.WEDO2_MOTOR_VALUE_WRITE, data); // this.send(data, Consts.BLECharacteristic.WEDO2_MOTOR_VALUE_WRITE);
return resolve(); // return resolve();
}, time); // }, time);
portObj.setEventTimer(timeout); // portObj.setEventTimer(timeout);
} else { // } else {
return resolve(); // return resolve();
} // }
}); // });
} // }
public send (uuid: string, message: Buffer, callback?: () => void) { public send (message: Buffer, uuid: string, callback?: () => void) {
if (debug.enabled) { if (debug.enabled) {
debug(`Sent Message (${this._getCharacteristicNameFromUUID(uuid)})`, message); debug(`Sent Message (${this._getCharacteristicNameFromUUID(uuid)})`, message);
} }
@ -299,12 +299,12 @@ export class WeDo2SmartHub extends Hub {
protected _activatePortDevice (port: number, type: number, mode: number, format: number, callback?: () => void) { protected _activatePortDevice (port: number, type: number, mode: number, format: number, callback?: () => void) {
this.send(Consts.BLECharacteristic.WEDO2_PORT_TYPE_WRITE, Buffer.from([0x01, 0x02, port, type, mode, 0x01, 0x00, 0x00, 0x00, format, 0x01]), callback); this.send(Buffer.from([0x01, 0x02, port, type, mode, 0x01, 0x00, 0x00, 0x00, format, 0x01]), Consts.BLECharacteristic.WEDO2_PORT_TYPE_WRITE, callback);
} }
protected _deactivatePortDevice (port: number, type: number, mode: number, format: number, callback?: () => void) { protected _deactivatePortDevice (port: number, type: number, mode: number, format: number, callback?: () => void) {
this.send(Consts.BLECharacteristic.WEDO2_PORT_TYPE_WRITE, Buffer.from([0x01, 0x02, port, type, mode, 0x01, 0x00, 0x00, 0x00, format, 0x00]), callback); this.send(Buffer.from([0x01, 0x02, port, type, mode, 0x01, 0x00, 0x00, 0x00, format, 0x00]), Consts.BLECharacteristic.WEDO2_PORT_TYPE_WRITE, callback);
} }
@ -361,7 +361,7 @@ export class WeDo2SmartHub extends Hub {
break; break;
} }
this._registerDeviceAttachment(device); this._attachDevice(device);
} }
@ -379,101 +379,101 @@ export class WeDo2SmartHub extends Hub {
private _parseSensorMessage (data: Buffer) { private _parseSensorMessage (data: Buffer) {
debug("Received Message (WEDO2_SENSOR_VALUE)", data); // debug("Received Message (WEDO2_SENSOR_VALUE)", data);
if (data[0] === 0x01) { // if (data[0] === 0x01) {
/** // /**
* Emits when a button is pressed. // * Emits when a button is pressed.
* @event WeDo2SmartHub#button // * @event WeDo2SmartHub#button
* @param {string} button // * @param {string} button
* @param {ButtonState} state // * @param {ButtonState} state
*/ // */
this.emit("button", "GREEN", Consts.ButtonState.PRESSED); // this.emit("button", "GREEN", Consts.ButtonState.PRESSED);
return; // return;
} else if (data[0] === 0x00) { // } else if (data[0] === 0x00) {
this.emit("button", "GREEN", Consts.ButtonState.RELEASED); // this.emit("button", "GREEN", Consts.ButtonState.RELEASED);
return; // return;
} // }
// Voltage // // Voltage
if (data[1] === 0x03) { // if (data[1] === 0x03) {
const voltage = data.readInt16LE(2); // const voltage = data.readInt16LE(2);
this._voltage = voltage / 40; // this._voltage = voltage / 40;
// Current // // Current
} else if (data[1] === 0x04) { // } else if (data[1] === 0x04) {
const current = data.readInt16LE(2); // const current = data.readInt16LE(2);
this._current = current / 1000; // this._current = current / 1000;
} // }
const port = this._getPortForPortNumber(data[1]); // const port = this._getPortForPortNumber(data[1]);
if (!port) { // if (!port) {
return; // return;
} // }
if (port && port.connected) { // if (port && port.connected) {
switch (port.type) { // switch (port.type) {
case Consts.DeviceType.WEDO2_DISTANCE: { // case Consts.DeviceType.WEDO2_DISTANCE: {
let distance = data[2]; // let distance = data[2];
if (data[3] === 1) { // if (data[3] === 1) {
distance = data[2] + 255; // distance = data[2] + 255;
} // }
/** // /**
* Emits when a distance sensor is activated. // * Emits when a distance sensor is activated.
* @event WeDo2SmartHub#distance // * @event WeDo2SmartHub#distance
* @param {string} port // * @param {string} port
* @param {number} distance Distance, in millimeters. // * @param {number} distance Distance, in millimeters.
*/ // */
this.emit("distance", port.id, distance * 10); // this.emit("distance", port.id, distance * 10);
break; // break;
} // }
case Consts.DeviceType.COLOR_DISTANCE_SENSOR: { // case Consts.DeviceType.COLOR_DISTANCE_SENSOR: {
const distance = data[2]; // const distance = data[2];
/** // /**
* Emits when a color sensor is activated. // * Emits when a color sensor is activated.
* @event WeDo2SmartHub#color // * @event WeDo2SmartHub#color
* @param {string} port // * @param {string} port
* @param {Color} color // * @param {Color} color
*/ // */
this.emit("color", port.id, distance); // this.emit("color", port.id, distance);
break; // break;
} // }
case Consts.DeviceType.WEDO2_TILT: { // case Consts.DeviceType.WEDO2_TILT: {
this._lastTiltX = data.readInt8(2); // this._lastTiltX = data.readInt8(2);
this._lastTiltY = data.readInt8(3); // this._lastTiltY = data.readInt8(3);
/** // /**
* Emits when a tilt sensor is activated. // * Emits when a tilt sensor is activated.
* @event WeDo2SmartHub#tilt // * @event WeDo2SmartHub#tilt
* @param {string} port // * @param {string} port
* @param {number} x // * @param {number} x
* @param {number} y // * @param {number} y
*/ // */
this.emit("tilt", port.id, this._lastTiltX, this._lastTiltY); // this.emit("tilt", port.id, this._lastTiltX, this._lastTiltY);
break; // break;
} // }
case Consts.DeviceType.BOOST_TACHO_MOTOR: { // case Consts.DeviceType.BOOST_TACHO_MOTOR: {
const rotation = data.readInt32LE(2); // const rotation = data.readInt32LE(2);
/** // /**
* Emits when a rotation sensor is activated. // * Emits when a rotation sensor is activated.
* @event WeDo2SmartHub#rotate // * @event WeDo2SmartHub#rotate
* @param {string} port // * @param {string} port
* @param {number} rotation // * @param {number} rotation
*/ // */
this.emit("rotate", port.id, rotation); // this.emit("rotate", port.id, rotation);
break; // break;
} // }
case Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR: { // case Consts.DeviceType.CONTROL_PLUS_LARGE_MOTOR: {
const rotation = data.readInt32LE(2); // const rotation = data.readInt32LE(2);
this.emit("rotate", port.id, rotation); // this.emit("rotate", port.id, rotation);
break; // break;
} // }
case Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR: { // case Consts.DeviceType.CONTROL_PLUS_XLARGE_MOTOR: {
const rotation = data.readInt32LE(2); // const rotation = data.readInt32LE(2);
this.emit("rotate", port.id, rotation); // this.emit("rotate", port.id, rotation);
break; // break;
} // }
} // }
} // }
} }