diff --git a/src/consts.ts b/src/consts.ts index 0c66aba9..f15f2c48 100644 --- a/src/consts.ts +++ b/src/consts.ts @@ -1,90 +1,3 @@ -/** - * @typedef HubType - * @property {number} UNKNOWN 0 - * @property {number} WEDO2_SMART_HUB 1 - * @property {number} MOVE_HUB 2 - * @property {number} POWERED_UP_HUB 3 - * @property {number} POWERED_UP_REMOTE 4 - * @property {number} DUPLO_TRAIN_HUB 5 - * @property {number} CONTROL_PLUS_HUB 6 - */ -export enum HubType { - UNKNOWN = 0, - WEDO2_SMART_HUB = 1, - MOVE_HUB = 2, - HUB = 3, - REMOTE_CONTROL = 4, - DUPLO_TRAIN_BASE = 5, - TECHNIC_MEDIUM_HUB = 6 -} - - -// tslint:disable-next-line -export const HubTypeNames = HubType; - - -/** - * @typedef DeviceType - * @property {number} UNKNOWN 0 - * @property {number} SIMPLE_MEDIUM_LINEAR_MOTOR 1 - * @property {number} TRAIN_MOTOR 2 - * @property {number} LED_LIGHTS 8 - * @property {number} VOLTAGE 20 - * @property {number} CURRENT 21 - * @property {number} PIEZO_TONE 22 - * @property {number} RGB_LIGHT 23 - * @property {number} WEDO2_TILT 34 - * @property {number} WEDO2_DISTANCE 35 - * @property {number} COLOR_DISTANCE_SENSOR 37 - * @property {number} MEDIUM_LINEAR_MOTOR 38 - * @property {number} MOVE_HUB_MEDIUM_LINEAR_MOTOR 39 - * @property {number} BOOST_TILT 40 - * @property {number} DUPLO_TRAIN_BASE_MOTOR 41 - * @property {number} DUPLO_TRAIN_BASE_SPEAKER 42 - * @property {number} DUPLO_TRAIN_BASE_COLOR 43 - * @property {number} DUPLO_TRAIN_BASE_SPEEDOMETER 44 - * @property {number} CONTROL_PLUS_LARGE_MOTOR 46 - * @property {number} CONTROL_PLUS_XLARGE_MOTOR 47 - * @property {number} POWERED_UP_REMOTE_BUTTON 55 - * @property {number} RSSI 56 - * @property {number} CONTROL_PLUS_ACCELEROMETER 58 - * @property {number} CONTROL_PLUS_TILT 59 - */ -export enum DeviceType { - UNKNOWN = 0, - SIMPLE_MEDIUM_LINEAR_MOTOR = 1, - TRAIN_MOTOR = 2, - LIGHT = 8, - VOLTAGE_SENSOR = 20, - CURRENT_SENSOR = 21, - PIEZO_BUZZER = 22, - HUB_LED = 23, - TILT_SENSOR = 34, - MOTION_SENSOR = 35, - COLOR_DISTANCE_SENSOR = 37, - MEDIUM_LINEAR_MOTOR = 38, - MOVE_HUB_MEDIUM_LINEAR_MOTOR = 39, - MOVE_HUB_TILT_SENSOR = 40, - DUPLO_TRAIN_BASE_MOTOR = 41, - DUPLO_TRAIN_BASE_SPEAKER = 42, - DUPLO_TRAIN_BASE_COLOR_SENSOR = 43, - DUPLO_TRAIN_BASE_SPEEDOMETER = 44, - TECHNIC_LARGE_LINEAR_MOTOR = 46, - TECHNIC_XLARGE_LINEAR_MOTOR = 47, - TECHNIC_MEDIUM_HUB_GEST_SENSOR = 54, - REMOTE_CONTROL_BUTTON = 55, - REMOTE_CONTROL_RSSI = 56, - TECHNIC_MEDIUM_HUB_ACCELEROMETER = 57, - TECHNIC_MEDIUM_HUB_GYRO_SENSOR = 58, - TECHNIC_MEDIUM_HUB_TILT_SENSOR = 59, - TECHNIC_MEDIUM_HUB_TEMPERATURE_SENSOR = 60, -} - - -// tslint:disable-next-line -export const DeviceTypeNames = DeviceType; - - /** * @typedef Color * @property {number} BLACK 0 @@ -189,3 +102,24 @@ export enum BLECharacteristic { WEDO2_NAME_ID = "00001524-1212-efde-1523-785feabcd123", // "1524" LPF2_ALL = "00001624-1212-efde-1623-785feabcd123" } + +export enum ValueType { + UInt8 = "UInt8", + Int8 = "Int8", + UInt16 = "UInt16", + Int16 = "Int16", + UInt32 = "UInt32", + Int32 = "Int32", + Float = "Float" +} + +// tslint:disable-next-line +export const ValueBits = { + [ValueType.UInt8]: 1, + [ValueType.Int8]: 1, + [ValueType.UInt16]: 2, + [ValueType.Int16]: 2, + [ValueType.UInt32]: 4, + [ValueType.Int32]: 4, + [ValueType.Float]: 4 +}; diff --git a/src/devices.ts b/src/devices.ts new file mode 100644 index 00000000..826f1db3 --- /dev/null +++ b/src/devices.ts @@ -0,0 +1,93 @@ +import { Device } from "./devices/generic/device"; +export { Device }; + +import { ColorDistanceSensor } from "./devices/colordistancesensor"; +import { CurrentSensor } from "./devices/currentsensor"; +import { DuploTrainBaseColorSensor } from "./devices/duplotrainbasecolorsensor"; +import { DuploTrainBaseMotor } from "./devices/duplotrainbasemotor"; +import { DuploTrainBaseSpeaker } from "./devices/duplotrainbasespeaker"; +import { DuploTrainBaseSpeedometer } from "./devices/duplotrainbasespeedometer"; +import { HubLED } from "./devices/hubled"; +import { Light } from "./devices/light"; +import { MediumLinearMotor } from "./devices/mediumlinearmotor"; +import { MotionSensor } from "./devices/motionsensor"; +import { MoveHubMediumLinearMotor } from "./devices/movehubmediumlinearmotor"; +import { MoveHubTiltSensor } from "./devices/movehubtiltsensor"; +import { PiezoBuzzer } from "./devices/piezobuzzer"; +import { RemoteControlButton } from "./devices/remotecontrolbutton"; +import { SimpleMediumLinearMotor } from "./devices/simplemediumlinearmotor"; +import { TechnicLargeLinearMotor } from "./devices/techniclargelinearmotor"; +import { TechnicMediumHubAccelerometerSensor } from "./devices/technicmediumhubaccelerometersensor"; +import { TechnicMediumHubGyroSensor } from "./devices/technicmediumhubgyrosensor"; +import { TechnicMediumHubTiltSensor } from "./devices/technicmediumhubtiltsensor"; +import { TechnicXLargeLinearMotor } from "./devices/technicxlargelinearmotor"; +import { TiltSensor } from "./devices/tiltsensor"; +import { TrainMotor } from "./devices/trainmotor"; +import { VoltageSensor } from "./devices/voltagesensor"; + +export { + ColorDistanceSensor, + CurrentSensor, + DuploTrainBaseColorSensor, + DuploTrainBaseMotor, + DuploTrainBaseSpeaker, + DuploTrainBaseSpeedometer, + HubLED, + Light, + MediumLinearMotor, + MotionSensor, + MoveHubMediumLinearMotor, + MoveHubTiltSensor, + PiezoBuzzer, + RemoteControlButton, + SimpleMediumLinearMotor, + TechnicLargeLinearMotor, + TechnicMediumHubAccelerometerSensor, + TechnicMediumHubGyroSensor, + TechnicMediumHubTiltSensor, + TechnicXLargeLinearMotor, + TiltSensor, + TrainMotor, + VoltageSensor, +}; + +export const devices: {[type: number]: typeof Device} = { + [SimpleMediumLinearMotor.type]: SimpleMediumLinearMotor, + [TrainMotor.type]: TrainMotor, + [Light.type]: Light, + [VoltageSensor.type]: VoltageSensor, + [CurrentSensor.type]: CurrentSensor, + [PiezoBuzzer.type]: PiezoBuzzer, + [HubLED.type]: HubLED, + [TiltSensor.type]: TiltSensor, + [MotionSensor.type]: MotionSensor, + [ColorDistanceSensor.type]: ColorDistanceSensor, + [MediumLinearMotor.type]: MediumLinearMotor, + [MoveHubMediumLinearMotor.type]: MoveHubMediumLinearMotor, + [MoveHubTiltSensor.type]: MoveHubTiltSensor, + [DuploTrainBaseMotor.type]: DuploTrainBaseMotor, + [DuploTrainBaseSpeaker.type]: DuploTrainBaseSpeaker, + [DuploTrainBaseColorSensor.type]: DuploTrainBaseColorSensor, + [DuploTrainBaseSpeedometer.type]: DuploTrainBaseSpeedometer, + [TechnicLargeLinearMotor.type]: TechnicLargeLinearMotor, + [TechnicXLargeLinearMotor.type]: TechnicXLargeLinearMotor, + [RemoteControlButton.type]: RemoteControlButton, + [TechnicMediumHubAccelerometerSensor.type]: TechnicMediumHubAccelerometerSensor, + [TechnicMediumHubGyroSensor.type]: TechnicMediumHubGyroSensor, + [TechnicMediumHubTiltSensor.type]: TechnicMediumHubTiltSensor, +}; + +export const deviceType: {[typeName: string]: number} = { + UNKNOW: 0, +}; +export const deviceTypeNames: {[typeName: number]: string } = { + 0: "UNKNOW", +}; + +for (const type in devices) { + if (devices[+type]) { + const device = devices[+type]; + deviceType[device.typeName] = device.type; + deviceTypeNames[device.type] = device.typeName; + } +} diff --git a/src/devices/colordistancesensor.ts b/src/devices/colordistancesensor.ts index 26ac2b36..e126e3f2 100644 --- a/src/devices/colordistancesensor.ts +++ b/src/devices/colordistancesensor.ts @@ -1,91 +1,124 @@ -import { Device } from "./device"; +import { Device, DeviceVersion } from "./generic/device"; -import { IDeviceInterface } from "../interfaces"; +import { IDeviceInterface, IDeviceMode } from "../interfaces"; -import * as Consts from "../consts"; +import { HubType, ValueType } from "../consts"; export class ColorDistanceSensor extends Device { + protected static _type = 37; + protected static _typeName: string = "COLOR_DISTANCE_SENSOR"; - constructor (hub: IDeviceInterface, portId: number) { - super(hub, portId, ColorDistanceSensor.ModeMap, Consts.DeviceType.COLOR_DISTANCE_SENSOR); + constructor (hub: IDeviceInterface, portId: number, versions: DeviceVersion) { + super(hub, portId, versions, ColorDistanceSensor.modes); } public receive (message: Buffer) { - const mode = this._mode; - - switch (mode) { - case ColorDistanceSensor.Mode.COLOR: - if (message[this.isWeDo2SmartHub ? 2 : 4] <= 10) { - const color = message[this.isWeDo2SmartHub ? 2 : 4]; - - /** - * Emits when a color sensor is activated. - * @event ColorDistanceSensor#color - * @param {Color} color - */ - this.emitGlobal("color", color); - } - break; - - case ColorDistanceSensor.Mode.DISTANCE: - if (this.isWeDo2SmartHub) { - break; - } - if (message[4] <= 10) { - const distance = Math.floor(message[4] * 25.4) - 20; - - /** - * Emits when a distance sensor is activated. - * @event ColorDistanceSensor#distance - * @param {number} distance Distance, in millimeters. - */ - this.emitGlobal("distance", distance); - } - break; - - case ColorDistanceSensor.Mode.COLOR_AND_DISTANCE: - if (this.isWeDo2SmartHub) { - break; - } - - let distance = message[5]; - const partial = message[7]; - - if (partial > 0) { - distance += 1.0 / partial; - } - - distance = Math.floor(distance * 25.4) - 20; - - /** - * A combined color and distance event, emits when the sensor is activated. - * @event ColorDistanceSensor#colorAndDistance - * @param {Color} color - * @param {number} distance Distance, in millimeters. - */ - if (message[4] <= 10) { - const color = message[4]; - this.emitGlobal("colorAndDistance", color, distance); - } - break; + const data = super.receive(message) || []; + if (this._mode === "SPEC_1") { + this.emit("color", data[0]); + this.emit("distance", data[1]); + this.emit("reflectivity", data[3]); } - } + return data; + } } export namespace ColorDistanceSensor { - - export enum Mode { - COLOR = 0x00, - DISTANCE = 0x01, - COLOR_AND_DISTANCE = 0x08 - } - - export const ModeMap: {[event: string]: number} = { - "color": ColorDistanceSensor.Mode.COLOR, - "distance": ColorDistanceSensor.Mode.DISTANCE, - "colorAndDistance": ColorDistanceSensor.Mode.COLOR_AND_DISTANCE - } - -} \ No newline at end of file + export const modes: { [name: string]: IDeviceMode } = { + COLOR: { + /** + * Emits when a color sensor is activated. + * @event ColorDistanceSensor#color + * @param {string} port + * @param {Color} color + */ + input: true, + event: "color", + values: { type: ValueType.UInt8, count: 1, min: 0, max: 255 }, + num: { + [HubType.MOVE_HUB]: 0x00, + [HubType.TECHNIC_MEDIUM_HUB]: 0x00, + [HubType.HUB]: 0x00, + [HubType.WEDO2_SMART_HUB]: 0x00 + } + }, + PROX: { + input: true, + event: "distance", + values: { type: ValueType.UInt8, count: 1, min: 0, max: 10 }, + num: { + [HubType.MOVE_HUB]: 0x01, + [HubType.TECHNIC_MEDIUM_HUB]: 0x01, + [HubType.HUB]: 0x01 + } + }, + COUNT: { + input: true, + event: "count", + values: { type: ValueType.UInt8, count: 1, min: 0 }, + num: { + [HubType.MOVE_HUB]: 0x02, + [HubType.TECHNIC_MEDIUM_HUB]: 0x02, + [HubType.HUB]: 0x02 + } + }, + REFLT: { + input: true, + event: "reflectivity", + values: { type: ValueType.UInt8, count: 1, min: 0, max: 100 }, + num: { + [HubType.MOVE_HUB]: 0x03, + [HubType.TECHNIC_MEDIUM_HUB]: 0x03, + [HubType.HUB]: 0x03 + } + }, + AMBI: { + input: true, + event: "luminosity", + values: { type: ValueType.UInt8, count: 1, min: 0, max: 100 }, + num: { + [HubType.MOVE_HUB]: 0x04, + [HubType.TECHNIC_MEDIUM_HUB]: 0x04, + [HubType.HUB]: 0x04 + } + }, + COL_O: { + input: false, + num: { + [HubType.MOVE_HUB]: 0x05, + [HubType.TECHNIC_MEDIUM_HUB]: 0x05, + [HubType.HUB]: 0x05 + } + }, + RGB_I: { + input: true, + event: "rgb", + values: { type: ValueType.UInt8, count: 3, min: 0, max: 255 }, + num: { + [HubType.MOVE_HUB]: 0x06, + [HubType.TECHNIC_MEDIUM_HUB]: 0x06, + [HubType.HUB]: 0x06 + } + }, + IR_Tx: { + input: false, + num: { + [HubType.MOVE_HUB]: 0x07, + [HubType.TECHNIC_MEDIUM_HUB]: 0x07, + [HubType.HUB]: 0x07 + } + }, + SPEC_1: { + input: true, + event: "colorAndDistance", + values: { type: ValueType.UInt8, count: 4, min: 0, max: 255 }, + num: { + [HubType.MOVE_HUB]: 0x08, + [HubType.TECHNIC_MEDIUM_HUB]: 0x08, + [HubType.HUB]: 0x08 + } + } + }; +} diff --git a/src/devices/currentsensor.ts b/src/devices/currentsensor.ts index 69ef5499..e96a124f 100644 --- a/src/devices/currentsensor.ts +++ b/src/devices/currentsensor.ts @@ -1,63 +1,48 @@ -import { Device } from "./device"; +import { Device, DeviceVersion } from "./generic/device"; -import { IDeviceInterface } from "../interfaces"; +import { IDeviceInterface, IDeviceMode } from "../interfaces"; -import * as Consts from "../consts"; +import { HubType, ValueType } from "../consts"; export class CurrentSensor extends Device { + protected static _type = 21; - constructor (hub: IDeviceInterface, portId: number) { - super(hub, portId, CurrentSensor.ModeMap, Consts.DeviceType.CURRENT_SENSOR); + constructor (hub: IDeviceInterface, portId: number, versions: DeviceVersion) { + super(hub, portId, versions, CurrentSensor.modes); } - - public receive (message: Buffer) { - const mode = this.mode; - - switch (mode) { - case CurrentSensor.Mode.CURRENT: - if (this.isWeDo2SmartHub) { - const current = message.readInt16LE(2) / 1000; - this.emitGlobal("current", current); - } else { - let maxCurrentValue = CurrentSensor.MaxCurrentValue[this.hub.type]; - if (maxCurrentValue === undefined) { - maxCurrentValue = CurrentSensor.MaxCurrentValue[Consts.HubType.UNKNOWN]; - } - let maxCurrentRaw = CurrentSensor.MaxCurrentRaw[this.hub.type]; - if (maxCurrentRaw === undefined) { - maxCurrentRaw = CurrentSensor.MaxCurrentRaw[Consts.HubType.UNKNOWN]; - } - const current = message.readUInt16LE(4) * maxCurrentValue / maxCurrentRaw; - /** - * Emits when a current change is detected. - * @event CurrentSensor#current - * @param {number} current - */ - this.emitGlobal("current", current); - } - break; - } - } - } export namespace CurrentSensor { - - export enum Mode { - CURRENT = 0x00 - } - - export const ModeMap: {[event: string]: number} = { - "current": CurrentSensor.Mode.CURRENT - } + export const modes: { [name: string]: IDeviceMode } = { + CURRENT: { + /** + * Emits when a current change is detected. + * @event CurrentSensor#current + * @param {number} current + */ + input: true, + event: "current", + values: { type: ValueType.UInt8, count: 1, min: 0, max: 255 }, + num: { + [HubType.MOVE_HUB]: 0x00, + [HubType.TECHNIC_MEDIUM_HUB]: 0x00, + [HubType.HUB]: 0x00, + [HubType.WEDO2_SMART_HUB]: 0x00 + }, + transform(hubType, data) { + const maxCurrentValue = CurrentSensor.MaxCurrentValue[hubType] || CurrentSensor.MaxCurrentValue[HubType.UNKNOWN]; + const maxCurrentRaw = CurrentSensor.MaxCurrentRaw[hubType] || CurrentSensor.MaxCurrentRaw[HubType.UNKNOWN]; + return [data[0] * maxCurrentValue / maxCurrentRaw]; + } + } + }; export const MaxCurrentValue: {[hubType: number]: number} = { - [Consts.HubType.UNKNOWN]: 2444, - [Consts.HubType.TECHNIC_MEDIUM_HUB]: 4175, + [HubType.UNKNOWN]: 2444, + [HubType.TECHNIC_MEDIUM_HUB]: 4175, } export const MaxCurrentRaw: {[hubType: number]: number} = { - [Consts.HubType.UNKNOWN]: 4095, + [HubType.UNKNOWN]: 4095, } - } \ No newline at end of file diff --git a/src/devices/device.ts b/src/devices/device.ts deleted file mode 100644 index e80850df..00000000 --- a/src/devices/device.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { EventEmitter } from "events"; - -import { IDeviceInterface } from "../interfaces"; - -import * as Consts from "../consts"; - -export class Device extends EventEmitter { - - public autoSubscribe: boolean = true; - - protected _mode: number | undefined; - protected _busy: boolean = false; - protected _finished: (() => void) | undefined; - - private _hub: IDeviceInterface; - private _portId: number; - private _connected: boolean = true; - private _type: Consts.DeviceType; - private _modeMap: {[event: string]: number} = {}; - - private _isWeDo2SmartHub: boolean; - - constructor (hub: IDeviceInterface, portId: number, modeMap: {[event: string]: number} = {}, type: Consts.DeviceType = Consts.DeviceType.UNKNOWN) { - super(); - this._hub = hub; - this._portId = portId; - this._type = type; - this._modeMap = modeMap; - this._isWeDo2SmartHub = (this.hub.type === Consts.HubType.WEDO2_SMART_HUB); - - const eventAttachListener = (event: string) => { - if (event === "detach") { - return; - } - if (this.autoSubscribe) { - if (this._modeMap[event] !== undefined) { - this.subscribe(this._modeMap[event]); - } - } - }; - - const deviceDetachListener = (device: Device) => { - if (device.portId === this.portId) { - this._connected = false; - this.hub.removeListener("detach", deviceDetachListener); - this.emit("detach"); - } - }; - - for (let event in this._modeMap) { - if (this.hub.listenerCount(event) > 0) { - eventAttachListener(event); - } - } - - this.hub.on("newListener", eventAttachListener); - this.on("newListener", eventAttachListener); - this.hub.on("detach", deviceDetachListener); - } - - public get connected () { - return this._connected; - } - - public get hub () { - return this._hub; - } - - public get portId () { - return this._portId; - } - - public get portName () { - return this.hub.getPortNameForPortId(this.portId); - } - - public get type () { - return this._type; - } - - public get mode () { - return this._mode; - } - - protected get isWeDo2SmartHub () { - return this._isWeDo2SmartHub; - } - - public writeDirect (mode: number, data: Buffer, callback?: () => void) { - if (this.isWeDo2SmartHub) { - this.send(Buffer.concat([Buffer.from([this.portId, 0x01, 0x02]), data]), Consts.BLECharacteristic.WEDO2_MOTOR_VALUE_WRITE); - } else { - this.send(Buffer.concat([Buffer.from([0x81, this.portId, 0x11, 0x51, mode]), data]), Consts.BLECharacteristic.LPF2_ALL, callback); - } - } - - public send (data: Buffer, characteristic: string = Consts.BLECharacteristic.LPF2_ALL, callback?: () => void) { - this._ensureConnected(); - this.hub.send(data, characteristic, callback); - } - - public subscribe (mode: number) { - this._ensureConnected(); - if (mode !== this._mode) { - this._mode = mode; - this.hub.subscribe(this.portId, this.type, mode); - } - } - - public receive (message: Buffer) { - this.emitGlobal("receive", message); - } - - public emitGlobal (event: string, ...args: any) { - this.emit(event, ...args); - if (this.hub.listenerCount(event) > 0) { - this.hub.emit(event, this, ...args); - } - } - - public finish () { - this._busy = false; - if (this._finished) { - this._finished(); - this._finished = undefined; - } - } - - private _ensureConnected () { - if (!this.connected) { - throw new Error("Device is not connected"); - } - } - -} diff --git a/src/devices/duplotrainbasecolorsensor.ts b/src/devices/duplotrainbasecolorsensor.ts index 4c3a8aba..cb19e5e4 100644 --- a/src/devices/duplotrainbasecolorsensor.ts +++ b/src/devices/duplotrainbasecolorsensor.ts @@ -1,13 +1,12 @@ -import { Device } from "./device"; +import { Device, DeviceVersion } from "./generic/device"; import { IDeviceInterface } from "../interfaces"; -import * as Consts from "../consts"; - export class DuploTrainBaseColorSensor extends Device { + protected static _type = 43; - constructor (hub: IDeviceInterface, portId: number) { - super(hub, portId, DuploTrainBaseColorSensor.ModeMap, Consts.DeviceType.DUPLO_TRAIN_BASE_COLOR_SENSOR); + constructor (hub: IDeviceInterface, portId: number, versions: DeviceVersion) { + super(hub, portId, versions, DuploTrainBaseColorSensor.ModeMap); } public receive (message: Buffer) { diff --git a/src/devices/duplotrainbasemotor.ts b/src/devices/duplotrainbasemotor.ts index 2466832e..2927f496 100644 --- a/src/devices/duplotrainbasemotor.ts +++ b/src/devices/duplotrainbasemotor.ts @@ -1,13 +1,15 @@ -import { BasicMotor } from "./basicmotor"; +import { BasicMotor } from "./generic/basicmotor"; +import { DeviceVersion } from "./generic/device"; import { IDeviceInterface } from "../interfaces"; import * as Consts from "../consts"; export class DuploTrainBaseMotor extends BasicMotor { + protected static _type = 41; - constructor (hub: IDeviceInterface, portId: number) { - super(hub, portId, {}, Consts.DeviceType.DUPLO_TRAIN_BASE_MOTOR); + constructor (hub: IDeviceInterface, portId: number, versions: DeviceVersion) { + super(hub, portId, versions, {}); } } diff --git a/src/devices/duplotrainbasespeaker.ts b/src/devices/duplotrainbasespeaker.ts index 1bd6be28..9809ef32 100644 --- a/src/devices/duplotrainbasespeaker.ts +++ b/src/devices/duplotrainbasespeaker.ts @@ -1,14 +1,15 @@ -import { Device } from "./device"; +import { Device, DeviceVersion } from "./generic/device"; import { IDeviceInterface } from "../interfaces"; import * as Consts from "../consts"; export class DuploTrainBaseSpeaker extends Device { + protected static _type = 42; - constructor (hub: IDeviceInterface, portId: number) { - super(hub, portId, {}, Consts.DeviceType.DUPLO_TRAIN_BASE_SPEAKER); + constructor (hub: IDeviceInterface, portId: number, versions: DeviceVersion) { + super(hub, portId, versions, {}); } diff --git a/src/devices/duplotrainbasespeedometer.ts b/src/devices/duplotrainbasespeedometer.ts index 780c49a9..26823b86 100644 --- a/src/devices/duplotrainbasespeedometer.ts +++ b/src/devices/duplotrainbasespeedometer.ts @@ -1,13 +1,12 @@ -import { Device } from "./device"; +import { Device, DeviceVersion } from "./generic/device"; import { IDeviceInterface } from "../interfaces"; -import * as Consts from "../consts"; - export class DuploTrainBaseSpeedometer extends Device { + protected static _type = 44; - constructor (hub: IDeviceInterface, portId: number) { - super(hub, portId, DuploTrainBaseSpeedometer.ModeMap, Consts.DeviceType.DUPLO_TRAIN_BASE_SPEEDOMETER); + constructor (hub: IDeviceInterface, portId: number, versions: DeviceVersion) { + super(hub, portId, versions, DuploTrainBaseSpeedometer.ModeMap); } public receive (message: Buffer) { diff --git a/src/devices/basicmotor.ts b/src/devices/generic/basicmotor.ts similarity index 68% rename from src/devices/basicmotor.ts rename to src/devices/generic/basicmotor.ts index eb3be90c..5f6c5438 100644 --- a/src/devices/basicmotor.ts +++ b/src/devices/generic/basicmotor.ts @@ -1,16 +1,14 @@ -import { Device } from "./device"; +import { Device, DeviceVersion } from "./device"; -import { IDeviceInterface } from "../interfaces"; +import { IDeviceInterface, IDeviceMode } from "../../interfaces"; -import * as Consts from "../consts"; - -import { mapSpeed } from "../utils"; +import { mapSpeed } from "../../utils"; export class BasicMotor extends Device { - constructor (hub: IDeviceInterface, portId: number, modeMap: {[event: string]: number}, type: Consts.DeviceType = Consts.DeviceType.UNKNOWN) { - super(hub, portId, modeMap, type); + constructor (hub: IDeviceInterface, portId: number, versions: DeviceVersion, modes: {[name: string]: IDeviceMode}) { + super(hub, portId, versions, modes); } diff --git a/src/devices/generic/device.ts b/src/devices/generic/device.ts new file mode 100644 index 00000000..ab57187c --- /dev/null +++ b/src/devices/generic/device.ts @@ -0,0 +1,269 @@ +import { EventEmitter } from "events"; + +import { IDeviceInterface, IDeviceMode } from "../../interfaces"; + +import { BLECharacteristic, HubType, ValueBits, ValueType } from "../../consts"; + +export type DeviceVersion = { + hardware: string, + software: string, +} + +export class Device extends EventEmitter { + protected static _type: number = 0; + protected static _typeName: string = "UNKNOW"; + + public autoSubscribe: boolean = true; + + protected _mode: string | undefined; + protected _busy: boolean = false; + protected _finished: (() => void) | undefined; + + private _hub: IDeviceInterface; + private _portId: number; + private _connected: boolean = true; + private _modes: {[name: string]: IDeviceMode} = {}; + private _eventMap: {[event: string]: string}; + + private _isWeDo2SmartHub: boolean; + + private _versions: DeviceVersion; + + constructor (hub: IDeviceInterface, portId: number, versions: DeviceVersion, modes: {[name: string]: IDeviceMode} = {}) { + super(); + this._hub = hub; + this._portId = portId; + this._modes = modes; + this._isWeDo2SmartHub = (this.hub.type === HubType.WEDO2_SMART_HUB); + this._versions = versions; + + this._eventMap = Object.keys(modes).reduce( + (map: {[event: string]: string}, name) => { + const mode = modes[name]; + if (mode.num[hub.type] !== undefined && mode.event) { + map[mode.event] = name; + } + return map; + }, + {} + ); + + const eventAttachListener = (event: string) => { + if (event === "detach") { + return; + } + if (this.autoSubscribe) { + if (!this._eventMap[event]) { + // TODO : error handling -> no mode for event + return; + } + + this.subscribe(this._eventMap[event]); + } + }; + + const deviceDetachListener = (device: Device) => { + if (device.portId === this.portId) { + this._connected = false; + this.hub.removeListener("detach", deviceDetachListener); + this.emit("detach"); + } + }; + + for (let event in this._eventMap) { + if (this.hub.listenerCount(event) > 0) { + eventAttachListener(event); + } + } + + this.hub.on("newListener", eventAttachListener); + this.on("newListener", eventAttachListener); + this.hub.on("detach", deviceDetachListener); + } + + public get connected () { + return this._connected; + } + + public get hub () { + return this._hub; + } + + public get portId () { + return this._portId; + } + + public get portName () { + return this.hub.getPortNameForPortId(this.portId); + } + + public static get type () { + return this._type; + } + + public static get typeName () { + return this._typeName; + } + + public get type () { + return this.constructor._type; + } + + public get typeName () { + return this.constructor._typeName; + } + + public get mode () { + return this._mode; + } + + public get hardwareVersion () { + return this._versions.hardware; + } + + public get softwareVersion () { + return this._versions.software; + } + + protected get isWeDo2SmartHub () { + return this._isWeDo2SmartHub; + } + + + public writeDirect (mode: number, data: Buffer, callback?: () => void) { + if (this.isWeDo2SmartHub) { + this.send(Buffer.concat([Buffer.from([this.portId, 0x01, 0x02]), data]), BLECharacteristic.WEDO2_MOTOR_VALUE_WRITE, callback); + } else { + this.send(Buffer.concat([Buffer.from([0x81, this.portId, 0x11, 0x51, mode]), data]), BLECharacteristic.LPF2_ALL, callback); + } + } + + protected subscribeAndWriteDirect (modeName: string, data: Buffer, callback?: () => void) { + const previousMode = this.mode; + const modeNum = this.subscribe(modeName); + if (modeNum !== undefined) { + this.writeDirect(modeNum, data, () => { + if (previousMode) { + this.subscribe(previousMode); + if (callback) { + callback(); + } + } + }); + } + } + + public send (data: Buffer, characteristic: string = BLECharacteristic.LPF2_ALL, callback?: () => void) { + this._ensureConnected(); + this.hub.send(data, characteristic, callback); + } + + protected sendLinearPowerCommand (value: number) { + return new Promise((resolve) => { + if (this.isWeDo2SmartHub) { + const data = Buffer.from([this.portId, 0x01, 0x02, value]); + this.send(data, BLECharacteristic.WEDO2_MOTOR_VALUE_WRITE); + } else { + const data = Buffer.from([0x81, this.portId, 0x11, 0x51, 0x00, value]); + this.send(data); + } + return resolve(); + }); + } + + public subscribe (modeName: string) { + this._ensureConnected(); + const modeNum = this._modes[modeName].num[this.hub.type]; + + if (modeName !== this._mode) { + this._mode = modeName; + + if (modeNum === undefined) { + // TODO : error handling -> unsupported mode + return; + } + this.hub.subscribe(this.portId, this.type, modeNum); + } + + return modeNum; + } + + public receive (message: Buffer) { + if (this._mode === undefined) { + // TODO : error handling -> no mode defined + return; + } + + const mode = this._modes[this._mode]; + + if (!mode.values) { + // TODO : error handling -> no parsing informations + return; + } + + const data = []; + + for (let index = 0; index <= message.length; index += ValueBits[mode.values.type]) { + switch (mode.values.type) { + case ValueType.UInt8: { + data.push(message.readUInt8(index)); + break; + } + case ValueType.Int8: { + data.push(message.readInt8(index)); + break; + } + case ValueType.UInt16: { + data.push(message.readUInt16LE(index)); + break; + } + case ValueType.Int16: { + data.push(message.readInt16LE(index)); + break; + } + case ValueType.UInt32: { + data.push(message.readUInt32LE(index)); + break; + } + case ValueType.Int32: { + data.push(message.readInt32LE(index)); + break; + } + case ValueType.Float: { + data.push(message.readFloatLE(index)); + break; + } + } + } + + if (mode.event) { + this.emitGlobal( + mode.event, + ...(mode.transform ? mode.transform(this.hub.type, data) : data) + ); + } + + return data; + } + + public emitGlobal (event: string, ...args: any) { + this.emit(event, ...args); + if (this.hub.listenerCount(event) > 0) { + this.hub.emit(event, this, ...args); + } + } + + public finish () { + this._busy = false; + if (this._finished) { + this._finished(); + this._finished = undefined; + } + } + + private _ensureConnected () { + if (!this.connected) { + throw new Error("Device is not connected"); + } + } +} diff --git a/src/devices/tachomotor.ts b/src/devices/generic/tachomotor.ts similarity index 83% rename from src/devices/tachomotor.ts rename to src/devices/generic/tachomotor.ts index 583f11c4..4662b4ca 100644 --- a/src/devices/tachomotor.ts +++ b/src/devices/generic/tachomotor.ts @@ -1,14 +1,14 @@ import { BasicMotor } from "./basicmotor"; -import { IDeviceInterface } from "../interfaces"; +import { IDeviceInterface } from "../../interfaces"; -import * as Consts from "../consts"; -import { mapSpeed } from "../utils"; +import { mapSpeed } from "../../utils"; +import { DeviceVersion } from "./device"; export class TachoMotor extends BasicMotor { - constructor (hub: IDeviceInterface, portId: number, modeMap: {[event: string]: number} = {}, type: Consts.DeviceType = Consts.DeviceType.UNKNOWN) { - super(hub, portId, Object.assign({}, modeMap, TachoMotor.ModeMap), type); + constructor (hub: IDeviceInterface, portId: number, versions: DeviceVersion, modeMap: {[event: string]: number} = {}) { + super(hub, portId, versions, Object.assign({}, modeMap, TachoMotor.ModeMap)); } public receive (message: Buffer) { diff --git a/src/devices/hubled.ts b/src/devices/hubled.ts index 807ba1fc..fa8bf71b 100644 --- a/src/devices/hubled.ts +++ b/src/devices/hubled.ts @@ -1,14 +1,16 @@ -import { Device } from "./device"; +import { Light } from "./light"; +import { DeviceVersion } from "./generic/device"; -import { IDeviceInterface } from "../interfaces"; +import { IDeviceInterface, IDeviceMode } from "../interfaces"; -import * as Consts from "../consts"; +import { BLECharacteristic, HubType } from "../consts"; -export class HubLED extends Device { +export class HubLED extends Light { + protected static _type = 23; - constructor (hub: IDeviceInterface, portId: number) { - super(hub, portId, {}, Consts.DeviceType.HUB_LED); + constructor (hub: IDeviceInterface, portId: number, versions: DeviceVersion) { + super(hub, portId, versions, HubLED.modes); } @@ -23,12 +25,12 @@ export class HubLED extends Device { if (typeof color === "boolean") { color = 0; } + if (this.isWeDo2SmartHub) { - this.send(Buffer.from([0x06, 0x17, 0x01, 0x01]), Consts.BLECharacteristic.WEDO2_PORT_TYPE_WRITE); - this.send(Buffer.from([0x06, 0x04, 0x01, color]), Consts.BLECharacteristic.WEDO2_MOTOR_VALUE_WRITE); + this.send(Buffer.from([0x06, 0x17, 0x01, 0x01]), BLECharacteristic.WEDO2_PORT_TYPE_WRITE); + this.send(Buffer.from([0x06, 0x04, 0x01, color]), BLECharacteristic.WEDO2_MOTOR_VALUE_WRITE); } else { - this.subscribe(HubLED.Mode.COLOR); - this.writeDirect(0x00, Buffer.from([color])); + this.subscribeAndWriteDirect("COLOR", Buffer.from([color])); } return resolve(); }); @@ -46,24 +48,35 @@ export class HubLED extends Device { public setRGB (red: number, green: number, blue: number) { return new Promise((resolve, reject) => { if (this.isWeDo2SmartHub) { - this.send(Buffer.from([0x06, 0x17, 0x01, 0x02]), Consts.BLECharacteristic.WEDO2_PORT_TYPE_WRITE); - this.send(Buffer.from([0x06, 0x04, 0x03, red, green, blue]), Consts.BLECharacteristic.WEDO2_MOTOR_VALUE_WRITE); + this.send(Buffer.from([0x06, 0x17, 0x01, 0x02]), BLECharacteristic.WEDO2_PORT_TYPE_WRITE); + this.send(Buffer.from([0x06, 0x04, 0x03, red, green, blue]), BLECharacteristic.WEDO2_MOTOR_VALUE_WRITE); } else { - this.subscribe(HubLED.Mode.RGB); - this.writeDirect(0x01, Buffer.from([red, green, blue])); + this.subscribeAndWriteDirect("RGB", Buffer.from([red, green, blue])); } return resolve(); }); } - - } export namespace HubLED { - - export enum Mode { - COLOR = 0x00, - RGB = 0x01 - } - + export const modes: { [name: string]: IDeviceMode } = { + COLOR: { + input: false, + num: { + [HubType.MOVE_HUB]: 0x00, + [HubType.TECHNIC_MEDIUM_HUB]: 0x00, + [HubType.HUB]: 0x00, + [HubType.WEDO2_SMART_HUB]: 0x00 + } + }, + RGB: { + input: false, + num: { + [HubType.MOVE_HUB]: 0x01, + [HubType.TECHNIC_MEDIUM_HUB]: 0x01, + [HubType.HUB]: 0x01, + [HubType.WEDO2_SMART_HUB]: 0x01 + } + } + }; } \ No newline at end of file diff --git a/src/devices/light.ts b/src/devices/light.ts index 8d2ae6b9..c78ed287 100644 --- a/src/devices/light.ts +++ b/src/devices/light.ts @@ -1,14 +1,13 @@ -import { Device } from "./device"; +import { Device, DeviceVersion } from "./generic/device"; -import { IDeviceInterface } from "../interfaces"; - -import * as Consts from "../consts"; +import { IDeviceInterface, IDeviceMode } from "../interfaces"; export class Light extends Device { + protected static _type = 8; - constructor (hub: IDeviceInterface, portId: number) { - super(hub, portId, {}, Consts.DeviceType.LIGHT); + constructor (hub: IDeviceInterface, portId: number, versions: DeviceVersion, modes: {[name: string]: IDeviceMode} = {}) { + super(hub, portId, versions, modes); } @@ -24,6 +23,4 @@ export class Light extends Device { return resolve(); }); } - - } diff --git a/src/devices/mediumlinearmotor.ts b/src/devices/mediumlinearmotor.ts index 48152ebf..7e90859d 100644 --- a/src/devices/mediumlinearmotor.ts +++ b/src/devices/mediumlinearmotor.ts @@ -1,13 +1,13 @@ -import { TachoMotor } from "./tachomotor"; +import { TachoMotor } from "./generic/tachomotor"; +import { DeviceVersion } from "./generic/device"; import { IDeviceInterface } from "../interfaces"; -import * as Consts from "../consts"; - export class MediumLinearMotor extends TachoMotor { + protected static _type = 38; - constructor (hub: IDeviceInterface, portId: number) { - super(hub, portId, {}, Consts.DeviceType.MEDIUM_LINEAR_MOTOR); + constructor (hub: IDeviceInterface, portId: number, versions: DeviceVersion) { + super(hub, portId, versions, {}); } } diff --git a/src/devices/motionsensor.ts b/src/devices/motionsensor.ts index a5d234b1..f5ff6b9f 100644 --- a/src/devices/motionsensor.ts +++ b/src/devices/motionsensor.ts @@ -1,13 +1,12 @@ -import { Device } from "./device"; +import { Device, DeviceVersion } from "./generic/device"; import { IDeviceInterface } from "../interfaces"; -import * as Consts from "../consts"; - export class MotionSensor extends Device { + protected static _type = 35; - constructor (hub: IDeviceInterface, portId: number) { - super(hub, portId, MotionSensor.ModeMap, Consts.DeviceType.MOTION_SENSOR); + constructor (hub: IDeviceInterface, portId: number, versions: DeviceVersion) { + super(hub, portId, versions, MotionSensor.ModeMap); } public receive (message: Buffer) { diff --git a/src/devices/movehubmediumlinearmotor.ts b/src/devices/movehubmediumlinearmotor.ts index 8b379660..af559f7a 100644 --- a/src/devices/movehubmediumlinearmotor.ts +++ b/src/devices/movehubmediumlinearmotor.ts @@ -1,13 +1,14 @@ -import { TachoMotor } from "./tachomotor"; +import { TachoMotor } from "./generic/tachomotor"; import { IDeviceInterface } from "../interfaces"; -import * as Consts from "../consts"; +import { DeviceVersion } from "./generic/device"; export class MoveHubMediumLinearMotor extends TachoMotor { + protected static _type = 39; - constructor (hub: IDeviceInterface, portId: number) { - super(hub, portId, {}, Consts.DeviceType.MOVE_HUB_MEDIUM_LINEAR_MOTOR); + constructor (hub: IDeviceInterface, portId: number, versions: DeviceVersion) { + super(hub, portId, versions, {}); } } diff --git a/src/devices/movehubtiltsensor.ts b/src/devices/movehubtiltsensor.ts index 08cb1b4c..5346052a 100644 --- a/src/devices/movehubtiltsensor.ts +++ b/src/devices/movehubtiltsensor.ts @@ -1,13 +1,14 @@ -import { Device } from "./device"; +import { Device, DeviceVersion } from "./generic/device"; import { IDeviceInterface } from "../interfaces"; import * as Consts from "../consts"; export class MoveHubTiltSensor extends Device { + protected static _type = 40; - constructor (hub: IDeviceInterface, portId: number) { - super(hub, portId, MoveHubTiltSensor.ModeMap, Consts.DeviceType.MOVE_HUB_TILT_SENSOR); + constructor (hub: IDeviceInterface, portId: number, versions: DeviceVersion) { + super(hub, portId, versions, MoveHubTiltSensor.ModeMap); } public receive (message: Buffer) { @@ -39,5 +40,5 @@ export namespace MoveHubTiltSensor { export const ModeMap: {[event: string]: number} = { "tilt": MoveHubTiltSensor.Mode.TILT } - + } \ No newline at end of file diff --git a/src/devices/piezobuzzer.ts b/src/devices/piezobuzzer.ts index 4c7eb02e..9d5830d5 100644 --- a/src/devices/piezobuzzer.ts +++ b/src/devices/piezobuzzer.ts @@ -1,19 +1,20 @@ -import { Device } from "./device"; +import { Device, DeviceVersion } from "./generic/device"; import { IDeviceInterface } from "../interfaces"; import * as Consts from "../consts"; export class PiezoBuzzer extends Device { + protected static _type = 22; - constructor (hub: IDeviceInterface, portId: number) { - super(hub, portId, {}, Consts.DeviceType.PIEZO_BUZZER); + constructor (hub: IDeviceInterface, portId: number, versions: DeviceVersion) { + super(hub, portId, versions); } /** - * Play a tone on the Hub's in-built buzzer + * Play a tone on the Hub"s in-built buzzer * @method PiezoBuzzer#playTone * @param {number} frequency * @param {number} time How long the tone should play for (in milliseconds). diff --git a/src/devices/remotecontrolbutton.ts b/src/devices/remotecontrolbutton.ts index 13d03878..1a5d0711 100644 --- a/src/devices/remotecontrolbutton.ts +++ b/src/devices/remotecontrolbutton.ts @@ -1,13 +1,12 @@ -import { Device } from "./device"; +import { Device, DeviceVersion } from "./generic/device"; import { IDeviceInterface } from "../interfaces"; -import * as Consts from "../consts"; - export class RemoteControlButton extends Device { + protected static _type = 55; - constructor (hub: IDeviceInterface, portId: number) { - super(hub, portId, RemoteControlButton.ModeMap, Consts.DeviceType.REMOTE_CONTROL_BUTTON); + constructor (hub: IDeviceInterface, portId: number, versions: DeviceVersion) { + super(hub, portId, versions, RemoteControlButton.ModeMap); } public receive (message: Buffer) { @@ -44,5 +43,5 @@ export namespace RemoteControlButton { "STOP": 0x7f, "RELEASED": 0x00, } - + } \ No newline at end of file diff --git a/src/devices/simplemediumlinearmotor.ts b/src/devices/simplemediumlinearmotor.ts index d41f21af..cf8a18ce 100644 --- a/src/devices/simplemediumlinearmotor.ts +++ b/src/devices/simplemediumlinearmotor.ts @@ -1,13 +1,14 @@ -import { BasicMotor } from "./basicmotor"; +import { BasicMotor } from "./generic/basicmotor"; import { IDeviceInterface } from "../interfaces"; -import * as Consts from "../consts"; +import { DeviceVersion } from "./generic/device"; export class SimpleMediumLinearMotor extends BasicMotor { + protected static _type = 1; - constructor (hub: IDeviceInterface, portId: number) { - super(hub, portId, {}, Consts.DeviceType.SIMPLE_MEDIUM_LINEAR_MOTOR); + constructor (hub: IDeviceInterface, portId: number, versions: DeviceVersion) { + super(hub, portId, versions, {}); } } diff --git a/src/devices/techniclargelinearmotor.ts b/src/devices/techniclargelinearmotor.ts index 103b3a00..ca4a57f1 100644 --- a/src/devices/techniclargelinearmotor.ts +++ b/src/devices/techniclargelinearmotor.ts @@ -1,13 +1,14 @@ -import { TachoMotor } from "./tachomotor"; +import { TachoMotor } from "./generic/tachomotor"; import { IDeviceInterface } from "../interfaces"; -import * as Consts from "../consts"; +import { DeviceVersion } from "./generic/device"; export class TechnicLargeLinearMotor extends TachoMotor { + protected static _type = 46; - constructor (hub: IDeviceInterface, portId: number) { - super(hub, portId, {}, Consts.DeviceType.TECHNIC_LARGE_LINEAR_MOTOR); + constructor (hub: IDeviceInterface, portId: number, versions: DeviceVersion) { + super(hub, portId, versions, {}); } } diff --git a/src/devices/technicmediumhubaccelerometersensor.ts b/src/devices/technicmediumhubaccelerometersensor.ts index 6beb42c8..26201acb 100644 --- a/src/devices/technicmediumhubaccelerometersensor.ts +++ b/src/devices/technicmediumhubaccelerometersensor.ts @@ -1,13 +1,14 @@ -import { Device } from "./device"; +import { Device, DeviceVersion } from "./generic/device"; import { IDeviceInterface } from "../interfaces"; import * as Consts from "../consts"; export class TechnicMediumHubAccelerometerSensor extends Device { + protected static _type = 57; - constructor (hub: IDeviceInterface, portId: number) { - super(hub, portId, TechnicMediumHubAccelerometerSensor.ModeMap, Consts.DeviceType.TECHNIC_MEDIUM_HUB_ACCELEROMETER); + constructor (hub: IDeviceInterface, portId: number, versions: DeviceVersion) { + super(hub, portId, versions, TechnicMediumHubAccelerometerSensor.ModeMap); } public receive (message: Buffer) { @@ -42,5 +43,5 @@ export namespace TechnicMediumHubAccelerometerSensor { export const ModeMap: {[event: string]: number} = { "accel": TechnicMediumHubAccelerometerSensor.Mode.ACCEL } - + } \ No newline at end of file diff --git a/src/devices/technicmediumhubgyrosensor.ts b/src/devices/technicmediumhubgyrosensor.ts index 9ecea0b0..e9156758 100644 --- a/src/devices/technicmediumhubgyrosensor.ts +++ b/src/devices/technicmediumhubgyrosensor.ts @@ -1,13 +1,12 @@ -import { Device } from "./device"; +import { Device, DeviceVersion } from "./generic/device"; import { IDeviceInterface } from "../interfaces"; -import * as Consts from "../consts"; - export class TechnicMediumHubGyroSensor extends Device { + protected static _type = 58; - constructor (hub: IDeviceInterface, portId: number) { - super(hub, portId, TechnicMediumHubGyroSensor.ModeMap, Consts.DeviceType.TECHNIC_MEDIUM_HUB_GYRO_SENSOR); + constructor (hub: IDeviceInterface, portId: number, versions: DeviceVersion) { + super(hub, portId, versions, TechnicMediumHubGyroSensor.ModeMap); } public receive (message: Buffer) { @@ -41,5 +40,5 @@ export namespace TechnicMediumHubGyroSensor { export const ModeMap: {[event: string]: number} = { "gyro": TechnicMediumHubGyroSensor.Mode.GYRO } - + } \ No newline at end of file diff --git a/src/devices/technicmediumhubtiltsensor.ts b/src/devices/technicmediumhubtiltsensor.ts index 47bd4c0e..b6737d0b 100644 --- a/src/devices/technicmediumhubtiltsensor.ts +++ b/src/devices/technicmediumhubtiltsensor.ts @@ -1,13 +1,14 @@ -import { Device } from "./device"; +import { Device, DeviceVersion } from "./generic/device"; import { IDeviceInterface } from "../interfaces"; import * as Consts from "../consts"; export class TechnicMediumHubTiltSensor extends Device { + protected static _type = 59; - constructor (hub: IDeviceInterface, portId: number) { - super(hub, portId, TechnicMediumHubTiltSensor.ModeMap, Consts.DeviceType.TECHNIC_MEDIUM_HUB_TILT_SENSOR); + constructor (hub: IDeviceInterface, portId: number, versions: DeviceVersion) { + super(hub, portId, versions, TechnicMediumHubTiltSensor.ModeMap); } public receive (message: Buffer) { @@ -41,5 +42,5 @@ export namespace TechnicMediumHubTiltSensor { export const ModeMap: {[event: string]: number} = { "tilt": TechnicMediumHubTiltSensor.Mode.TILT } - + } \ No newline at end of file diff --git a/src/devices/technicxlargelinearmotor.ts b/src/devices/technicxlargelinearmotor.ts index 53b4c0ac..0e2dc961 100644 --- a/src/devices/technicxlargelinearmotor.ts +++ b/src/devices/technicxlargelinearmotor.ts @@ -1,13 +1,15 @@ -import { TachoMotor } from "./tachomotor"; +import { TachoMotor } from "./generic/tachomotor"; import { IDeviceInterface } from "../interfaces"; import * as Consts from "../consts"; +import { DeviceVersion } from "./generic/device"; export class TechnicXLargeLinearMotor extends TachoMotor { + protected static _type = 47; - constructor (hub: IDeviceInterface, portId: number) { - super(hub, portId, {}, Consts.DeviceType.TECHNIC_XLARGE_LINEAR_MOTOR); + constructor (hub: IDeviceInterface, portId: number, versions: DeviceVersion) { + super(hub, portId, versions, {}); } } diff --git a/src/devices/tiltsensor.ts b/src/devices/tiltsensor.ts index 91c2a3d2..900d1ba2 100644 --- a/src/devices/tiltsensor.ts +++ b/src/devices/tiltsensor.ts @@ -1,13 +1,12 @@ -import { Device } from "./device"; +import { Device, DeviceVersion } from "./generic/device"; import { IDeviceInterface } from "../interfaces"; -import * as Consts from "../consts"; - export class TiltSensor extends Device { + protected static _type = 34; - constructor (hub: IDeviceInterface, portId: number) { - super(hub, portId, TiltSensor.ModeMap, Consts.DeviceType.TILT_SENSOR); + constructor (hub: IDeviceInterface, portId: number, versions: DeviceVersion) { + super(hub, portId, versions, TiltSensor.ModeMap); } public receive (message: Buffer) { @@ -39,5 +38,5 @@ export namespace TiltSensor { export const ModeMap: {[event: string]: number} = { "tilt": TiltSensor.Mode.TILT } - + } \ No newline at end of file diff --git a/src/devices/trainmotor.ts b/src/devices/trainmotor.ts index dc2996f3..5c4fc158 100644 --- a/src/devices/trainmotor.ts +++ b/src/devices/trainmotor.ts @@ -1,13 +1,11 @@ -import { BasicMotor } from "./basicmotor"; +import { BasicMotor } from "./generic/basicmotor"; +import { DeviceVersion } from "./generic/device"; import { IDeviceInterface } from "../interfaces"; -import * as Consts from "../consts"; - export class TrainMotor extends BasicMotor { - - constructor (hub: IDeviceInterface, portId: number) { - super(hub, portId, {}, Consts.DeviceType.TRAIN_MOTOR); + protected static _type = 2; + constructor (hub: IDeviceInterface, portId: number, versions: DeviceVersion) { + super(hub, portId, versions, {}); } - } diff --git a/src/devices/voltagesensor.ts b/src/devices/voltagesensor.ts index 8610e475..dce4f7e6 100644 --- a/src/devices/voltagesensor.ts +++ b/src/devices/voltagesensor.ts @@ -1,67 +1,53 @@ -import { Device } from "./device"; +import { Device, DeviceVersion } from "./generic/device"; -import { IDeviceInterface } from "../interfaces"; +import { IDeviceInterface, IDeviceMode } from "../interfaces"; -import * as Consts from "../consts"; +import { HubType, ValueType } from "../consts"; export class VoltageSensor extends Device { + protected static _type = 20; - constructor (hub: IDeviceInterface, portId: number) { - super(hub, portId, VoltageSensor.ModeMap, Consts.DeviceType.VOLTAGE_SENSOR); + constructor (hub: IDeviceInterface, portId: number, versions: DeviceVersion) { + super(hub, portId, versions, VoltageSensor.modes); } - - public receive (message: Buffer) { - const mode = this._mode; - - switch (mode) { - case VoltageSensor.Mode.VOLTAGE: - if (this.isWeDo2SmartHub) { - const voltage = message.readInt16LE(2) / 40; - this.emitGlobal("voltage", voltage); - } else { - let maxVoltageValue = VoltageSensor.MaxVoltageValue[this.hub.type]; - if (maxVoltageValue === undefined) { - maxVoltageValue = VoltageSensor.MaxVoltageValue[Consts.HubType.UNKNOWN]; - } - let maxVoltageRaw = VoltageSensor.MaxVoltageRaw[this.hub.type]; - if (maxVoltageRaw === undefined) { - maxVoltageRaw = VoltageSensor.MaxVoltageRaw[Consts.HubType.UNKNOWN]; - } - const voltage = message.readUInt16LE(4) * maxVoltageValue / maxVoltageRaw; - /** - * Emits when a voltage change is detected. - * @event VoltageSensor#voltage - * @param {number} voltage - */ - this.emitGlobal("voltage", voltage); - } - break; - } - } - } export namespace VoltageSensor { - - export enum Mode { - VOLTAGE = 0x00 - } - - export const ModeMap: {[event: string]: number} = { - "voltage": VoltageSensor.Mode.VOLTAGE - } + export const modes: { [name: string]: IDeviceMode } = { + VOLTAGE: { + /** + * Emits when a voltage change is detected. + * @event VoltageSensor#voltage + * @param {number} current + */ + input: true, + event: "voltage", + values: { type: ValueType.UInt8, count: 1, min: 0, max: 255 }, + num: { + [HubType.MOVE_HUB]: 0x00, + [HubType.TECHNIC_MEDIUM_HUB]: 0x00, + [HubType.HUB]: 0x00, + [HubType.WEDO2_SMART_HUB]: 0x00 + }, + transform(hubType, data) { + const maxVoltageValue = VoltageSensor.MaxVoltageValue[hubType] || VoltageSensor.MaxVoltageValue[HubType.UNKNOWN]; + const maxVoltageRaw = VoltageSensor.MaxVoltageRaw[hubType] || VoltageSensor.MaxVoltageRaw[HubType.UNKNOWN]; + return [data[0] * maxVoltageValue / maxVoltageRaw]; + } + } + }; export const MaxVoltageValue: {[hubType: number]: number} = { - [Consts.HubType.UNKNOWN]: 9.615, - [Consts.HubType.DUPLO_TRAIN_BASE]: 6.4, - [Consts.HubType.REMOTE_CONTROL]: 6.4, + [HubType.UNKNOWN]: 9.615, + [HubType.DUPLO_TRAIN_BASE]: 6.4, + [HubType.REMOTE_CONTROL]: 6.4, } export const MaxVoltageRaw: {[hubType: number]: number} = { - [Consts.HubType.UNKNOWN]: 3893, - [Consts.HubType.DUPLO_TRAIN_BASE]: 3047, - [Consts.HubType.REMOTE_CONTROL]: 3200, - [Consts.HubType.TECHNIC_MEDIUM_HUB]: 4095, + [HubType.UNKNOWN]: 3893, + [HubType.DUPLO_TRAIN_BASE]: 3047, + [HubType.REMOTE_CONTROL]: 3200, + [HubType.TECHNIC_MEDIUM_HUB]: 4095, } - + } \ No newline at end of file diff --git a/src/hubs.ts b/src/hubs.ts new file mode 100644 index 00000000..ba77867a --- /dev/null +++ b/src/hubs.ts @@ -0,0 +1,41 @@ +export { BaseHub } from "./hubs/generic/basehub"; + +import { DuploTrainBase } from "./hubs/duplotrainbase"; +import { Hub } from "./hubs/hub"; +import { MoveHub } from "./hubs/movehub"; +import { RemoteControl } from "./hubs/remotecontrol"; +import { TechnicMediumHub } from "./hubs/technicmediumhub"; +import { WeDo2SmartHub } from "./hubs/wedo2smarthub"; + +export { + DuploTrainBase, + Hub, + MoveHub, + RemoteControl, + TechnicMediumHub, + WeDo2SmartHub, +}; + +export const hubs = { + [WeDo2SmartHub.type]: WeDo2SmartHub, + [MoveHub.type]: MoveHub, + [Hub.type]: Hub, + [RemoteControl.type]: RemoteControl, + [DuploTrainBase.type]: DuploTrainBase, + [TechnicMediumHub.type]: TechnicMediumHub, +}; + +export const hubType: {[typeName: string]: number} = { + UNKNOW: 0, +}; +export const hubTypeNames: {[typeName: number]: string } = { + 0: "UNKNOW", +}; + +for (const type in hubs) { + if (hubs[+type]) { + const hub = hubs[+type]; + hubType[hub.typeName] = hub.type; + hubTypeNames[hub.type] = hub.typeName; + } +} diff --git a/src/hubs/duplotrainbase.ts b/src/hubs/duplotrainbase.ts index d9701a52..a62036eb 100644 --- a/src/hubs/duplotrainbase.ts +++ b/src/hubs/duplotrainbase.ts @@ -1,6 +1,6 @@ import { Peripheral } from "@abandonware/noble"; -import { LPF2Hub } from "./lpf2hub"; +import { LPF2Hub } from "./generic/lpf2hub"; import * as Consts from "../consts"; @@ -16,8 +16,6 @@ const debug = Debug("duplotrainbase"); * @extends BaseHub */ export class DuploTrainBase extends LPF2Hub { - - public static IsDuploTrainBase (peripheral: Peripheral) { return ( peripheral.advertisement && @@ -28,10 +26,17 @@ export class DuploTrainBase extends LPF2Hub { peripheral.advertisement.manufacturerData[3] === Consts.BLEManufacturerData.DUPLO_TRAIN_BASE_ID ); } - + + protected static _type: number = 5; + protected static _typeName: string = "DUPLO_TRAIN_BASE"; + protected static _portMap: {[portName: string]: number} = { + "MOTOR": 0, + "COLOR": 18, + "SPEEDOMETER": 19 + }; constructor (device: IBLEAbstraction) { - super(device, DuploTrainBase.PortMap, Consts.HubType.DUPLO_TRAIN_BASE); + super(device); debug("Discovered Duplo Train Base"); } @@ -47,13 +52,3 @@ export class DuploTrainBase extends LPF2Hub { } - -export namespace DuploTrainBase { - - export const PortMap: {[portName: string]: number} = { - "MOTOR": 0, - "COLOR": 18, - "SPEEDOMETER": 19 - } - -} \ No newline at end of file diff --git a/src/hubs/basehub.ts b/src/hubs/generic/basehub.ts similarity index 60% rename from src/hubs/basehub.ts rename to src/hubs/generic/basehub.ts index e7114557..c37a87d3 100644 --- a/src/hubs/basehub.ts +++ b/src/hubs/generic/basehub.ts @@ -1,45 +1,31 @@ import { EventEmitter } from "events"; -import { IBLEAbstraction } from "../interfaces"; - -import { ColorDistanceSensor } from "../devices/colordistancesensor"; -import { CurrentSensor } from "../devices/currentsensor"; -import { Device } from "../devices/device"; - -import { DuploTrainBaseColorSensor } from "../devices/duplotrainbasecolorsensor"; -import { DuploTrainBaseMotor } from "../devices/duplotrainbasemotor"; -import { DuploTrainBaseSpeaker } from "../devices/duplotrainbasespeaker"; -import { DuploTrainBaseSpeedometer } from "../devices/duplotrainbasespeedometer"; - -import { HubLED } from "../devices/hubled"; -import { Light } from "../devices/light"; -import { MediumLinearMotor } from "../devices/mediumlinearmotor"; -import { MotionSensor } from "../devices/motionsensor"; -import { MoveHubMediumLinearMotor } from "../devices/movehubmediumlinearmotor"; -import { MoveHubTiltSensor } from "../devices/movehubtiltsensor"; -import { RemoteControlButton } from "../devices/remotecontrolbutton"; -import { SimpleMediumLinearMotor } from "../devices/simplemediumlinearmotor"; -import { PiezoBuzzer } from "../devices/piezobuzzer"; -import { TechnicLargeLinearMotor } from "../devices/techniclargelinearmotor"; -import { TechnicMediumHubAccelerometerSensor } from "../devices/technicmediumhubaccelerometersensor"; -import { TechnicMediumHubGyroSensor } from "../devices/technicmediumhubgyrosensor"; -import { TechnicMediumHubTiltSensor } from "../devices/technicmediumhubtiltsensor"; -import { TechnicXLargeLinearMotor } from "../devices/technicxlargelinearmotor"; -import { TiltSensor } from "../devices/tiltsensor"; -import { TrainMotor } from "../devices/trainmotor"; -import { VoltageSensor } from "../devices/voltagesensor"; - -import * as Consts from "../consts"; +import { IBLEAbstraction } from "../../interfaces"; + +import { Device, DeviceVersion } from "../../devices/generic/device"; + +import { devices, deviceTypeNames } from "../../devices"; + +import * as Consts from "../../consts"; import Debug = require("debug"); const debug = Debug("basehub"); - /** * @class BaseHub * @extends EventEmitter */ export class BaseHub extends EventEmitter { + public static get type () { + return this._type; + } + public static get typeName () { + return this._typeName; + } + + protected static _type: number = 0; + protected static _typeName: string = "UNKNOW"; + protected static _portMap: {[portName: string]: number} = {}; protected _attachedDevices: {[portId: number]: Device} = {}; // protected _virtualPorts: {[portName: string]: Port} = {}; @@ -53,16 +39,12 @@ export class BaseHub extends EventEmitter { protected _bleDevice: IBLEAbstraction; - private _type: Consts.HubType; - private _portMap: {[portName: string]: number} = {}; - private _attachCallbacks: ((device: Device) => boolean)[] = []; + private _attachCallbacks: Array<((device: Device) => boolean)> = []; - constructor (device: IBLEAbstraction, portMap: {[portName: string]: number} = {}, type: Consts.HubType = Consts.HubType.UNKNOWN) { + constructor (device: IBLEAbstraction, portMap: {[portName: string]: number} = {}) { super(); this.setMaxListeners(20); // Technic Medium Hub has 9 built in devices + 4 external ports. Node.js throws a warning after 11 attached event listeners. - this._type = type; this._bleDevice = device; - this._portMap = portMap; device.on("disconnect", () => { /** * Emits when the hub is disconnected. @@ -87,16 +69,23 @@ export class BaseHub extends EventEmitter { * @property {string} type Hub type */ public get type () { - return this._type; + return this.constructor._type; } + /** + * @readonly + * @property {string} typeName Hub type name + */ + public get typeName () { + return this.constructor._typeName; + } /** * @readonly * @property {string[]} ports Array of port names */ public get ports () { - return Object.keys(this._portMap); + return Object.keys(this._getPortMap()); } @@ -184,7 +173,7 @@ export class BaseHub extends EventEmitter { public getDeviceAtPort (portName: string) { - const portId = this._portMap[portName]; + const portId = this._getPortMap()[portName]; if (portId !== undefined) { return this._attachedDevices[portId]; } else { @@ -234,14 +223,14 @@ export class BaseHub extends EventEmitter { } else { return false; } - }) + }); }); } public getPortNameForPortId (portId: number) { - for (const port of Object.keys(this._portMap)) { - if (this._portMap[port] === portId) { + for (const port of Object.keys(this._getPortMap())) { + if (this._getPortMap()[port] === portId) { return port; } } @@ -297,13 +286,13 @@ export class BaseHub extends EventEmitter { * @param {Device} device */ this.emit("attach", device); - debug(`Attached device type ${device.type} (${Consts.DeviceTypeNames[device.type]}) on port ${device.portName} (${device.portId})`); + debug(`Attached device type ${device.type} (${deviceTypeNames[device.type]}) on port ${device.portName} (${device.portId})`); let i = this._attachCallbacks.length; while (i--) { const callback = this._attachCallbacks[i]; if (callback(device)) { - this._attachCallbacks.splice(i, 1); + this._attachCallbacks.splice(i, 1); } } } @@ -317,45 +306,17 @@ export class BaseHub extends EventEmitter { * @param {Device} device */ this.emit("detach", device); - debug(`Detached device type ${device.type} (${Consts.DeviceTypeNames[device.type]}) on port ${device.portName} (${device.portId})`); + debug(`Detached device type ${device.type} (${deviceTypeNames[device.type]}) on port ${device.portName} (${device.portId})`); } - protected _createDevice (deviceType: number, portId: number) { - let constructor; - - // NK TODO: This needs to go in a better place - const deviceConstructors: {[type: number]: typeof Device} = { - [Consts.DeviceType.LIGHT]: Light, - [Consts.DeviceType.TRAIN_MOTOR]: TrainMotor, - [Consts.DeviceType.SIMPLE_MEDIUM_LINEAR_MOTOR]: SimpleMediumLinearMotor, - [Consts.DeviceType.MOVE_HUB_MEDIUM_LINEAR_MOTOR]: MoveHubMediumLinearMotor, - [Consts.DeviceType.MOTION_SENSOR]: MotionSensor, - [Consts.DeviceType.TILT_SENSOR]: TiltSensor, - [Consts.DeviceType.MOVE_HUB_TILT_SENSOR]: MoveHubTiltSensor, - [Consts.DeviceType.TECHNIC_MEDIUM_HUB_TILT_SENSOR]: TechnicMediumHubTiltSensor, - [Consts.DeviceType.TECHNIC_MEDIUM_HUB_GYRO_SENSOR]: TechnicMediumHubGyroSensor, - [Consts.DeviceType.TECHNIC_MEDIUM_HUB_ACCELEROMETER]: TechnicMediumHubAccelerometerSensor, - [Consts.DeviceType.MEDIUM_LINEAR_MOTOR]: MediumLinearMotor, - [Consts.DeviceType.TECHNIC_LARGE_LINEAR_MOTOR]: TechnicLargeLinearMotor, - [Consts.DeviceType.TECHNIC_XLARGE_LINEAR_MOTOR]: TechnicXLargeLinearMotor, - [Consts.DeviceType.COLOR_DISTANCE_SENSOR]: ColorDistanceSensor, - [Consts.DeviceType.VOLTAGE_SENSOR]: VoltageSensor, - [Consts.DeviceType.CURRENT_SENSOR]: CurrentSensor, - [Consts.DeviceType.REMOTE_CONTROL_BUTTON]: RemoteControlButton, - [Consts.DeviceType.HUB_LED]: HubLED, - [Consts.DeviceType.DUPLO_TRAIN_BASE_COLOR_SENSOR]: DuploTrainBaseColorSensor, - [Consts.DeviceType.DUPLO_TRAIN_BASE_MOTOR]: DuploTrainBaseMotor, - [Consts.DeviceType.DUPLO_TRAIN_BASE_SPEAKER]: DuploTrainBaseSpeaker, - [Consts.DeviceType.DUPLO_TRAIN_BASE_SPEEDOMETER]: DuploTrainBaseSpeedometer - }; - - constructor = deviceConstructors[deviceType as Consts.DeviceType]; - + protected _createDevice (deviceType: number, portId: number, versions: DeviceVersion) { + const constructor = devices[deviceType]; + if (constructor) { - return new constructor(this, portId); + return new constructor(this, portId, versions); } else { - return new Device(this, portId, undefined, deviceType); + return new Device(this, portId, versions, undefined); } } @@ -365,5 +326,8 @@ export class BaseHub extends EventEmitter { return this._attachedDevices[portId]; } + protected _getPortMap() { + return this.constructor._portMap; + } } diff --git a/src/hubs/lpf2hub.ts b/src/hubs/generic/lpf2hub.ts similarity index 96% rename from src/hubs/lpf2hub.ts rename to src/hubs/generic/lpf2hub.ts index 9d27ae74..8474ba25 100644 --- a/src/hubs/lpf2hub.ts +++ b/src/hubs/generic/lpf2hub.ts @@ -1,8 +1,10 @@ import { BaseHub } from "./basehub"; -import * as Consts from "../consts"; +import * as Consts from "../../consts"; -import { decodeMACAddress, decodeVersion, toBin, toHex } from "../utils"; +import { decodeMACAddress, decodeVersion, toBin, toHex } from "../../utils"; + +import { DeviceTypeNames } from "../../devices"; import Debug = require("debug"); const debug = Debug("lpf2hub"); @@ -67,7 +69,7 @@ export class LPF2Hub extends BaseHub { return new Promise((resolve, reject) => { let data = Buffer.from([0x01, 0x01, 0x01]); 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(data, Consts.BLECharacteristic.LPF2_ALL); this.send(data, Consts.BLECharacteristic.LPF2_ALL); this._name = name; @@ -247,17 +249,17 @@ export class LPF2Hub extends BaseHub { // Handle device attachments if (event === 0x01) { + const hwVersion = decodeVersion(message.readInt32LE(7)); + const swVersion = decodeVersion(message.readInt32LE(11)); if (modeInfoDebug.enabled) { - const deviceTypeName = Consts.DeviceTypeNames[message[5]] || "Unknown"; + const deviceTypeName = DeviceTypeNames[message[5]] || "Unknown"; modeInfoDebug(`Port ${toHex(portId)}, type ${toHex(deviceType, 4)} (${deviceTypeName})`); - const hwVersion = decodeVersion(message.readInt32LE(7)); - const swVersion = decodeVersion(message.readInt32LE(11)); modeInfoDebug(`Port ${toHex(portId)}, hardware version ${hwVersion}, software version ${swVersion}`); this._sendPortInformationRequest(portId); } - const device = this._createDevice(deviceType, portId); + const device = this._createDevice(deviceType, portId, { hardware: hwVersion, software: swVersion }); this._attachDevice(device); // Handle device detachments @@ -382,7 +384,8 @@ export class LPF2Hub extends BaseHub { const device = this._getDeviceByPortId(portId); if (device) { - device.receive(message); + // Forward message to device (without size, hub and port) + device.receive(message.slice(3)); } } diff --git a/src/hubs/hub.ts b/src/hubs/hub.ts index eb6cf295..7a706818 100644 --- a/src/hubs/hub.ts +++ b/src/hubs/hub.ts @@ -3,7 +3,7 @@ import compareVersion from "compare-versions"; import { IBLEAbstraction } from "../interfaces"; -import { LPF2Hub } from "./lpf2hub"; +import { LPF2Hub } from "./generic/lpf2hub"; import * as Consts from "../consts"; @@ -19,7 +19,6 @@ const debug = Debug("hub"); */ export class Hub extends LPF2Hub { - public static IsHub (peripheral: Peripheral) { return ( peripheral.advertisement && @@ -31,10 +30,20 @@ export class Hub extends LPF2Hub { ); } + protected static _type: number = 3; + protected static _typeName: string = "HUB"; + protected static _portMap: {[portName: string]: number} = { + "A": 0, + "B": 1, + "HUB_LED": 50, + "CURRENT_SENSOR": 59, + "VOLTAGE_SENSOR": 60 + }; + protected _currentPort = 0x3b; constructor (device: IBLEAbstraction) { - super(device, Hub.PortMap, Consts.HubType.HUB); + super(device); debug("Discovered Powered UP Hub"); } @@ -51,21 +60,9 @@ export class Hub extends LPF2Hub { protected _checkFirmware (version: string) { 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.`); } } } - -export namespace Hub { - - export const PortMap: {[portName: string]: number} = { - "A": 0, - "B": 1, - "HUB_LED": 50, - "CURRENT_SENSOR": 59, - "VOLTAGE_SENSOR": 60 - } - -} \ No newline at end of file diff --git a/src/hubs/movehub.ts b/src/hubs/movehub.ts index 657a083c..c3900201 100644 --- a/src/hubs/movehub.ts +++ b/src/hubs/movehub.ts @@ -3,7 +3,7 @@ import compareVersion from "compare-versions"; import { IBLEAbstraction } from "../interfaces"; -import { LPF2Hub } from "./lpf2hub"; +import { LPF2Hub } from "./generic/lpf2hub"; import * as Consts from "../consts"; @@ -19,7 +19,6 @@ const debug = Debug("movehub"); */ export class MoveHub extends LPF2Hub { - public static IsMoveHub (peripheral: Peripheral) { return ( peripheral.advertisement && @@ -31,8 +30,21 @@ export class MoveHub extends LPF2Hub { ); } + protected static _type: number = 2; + protected static _typeName: string = "MOVE_HUB"; + protected static _portMap: {[portName: string]: number} = { + "A": 0, + "B": 1, + "C": 2, + "D": 3, + "HUB_LED": 50, + "TILT_SENSOR": 58, + "CURRENT_SENSOR": 59, + "VOLTAGE_SENSOR": 60 + }; + constructor (device: IBLEAbstraction) { - super(device, MoveHub.PortMap, Consts.HubType.MOVE_HUB); + super(device); debug("Discovered Move Hub"); } @@ -49,24 +61,9 @@ export class MoveHub extends LPF2Hub { protected _checkFirmware (version: string) { if (compareVersion("2.0.00.0017", version) === 1) { - throw new Error(`Your Move 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 Move Hub"s (${this.name}) firmware is out of date and unsupported by this library. Please update it via the official Powered Up app.`); } } } - -export namespace MoveHub { - - export const PortMap: {[portName: string]: number} = { - "A": 0, - "B": 1, - "C": 2, - "D": 3, - "HUB_LED": 50, - "TILT_SENSOR": 58, - "CURRENT_SENSOR": 59, - "VOLTAGE_SENSOR": 60 - } - -} \ No newline at end of file diff --git a/src/hubs/remotecontrol.ts b/src/hubs/remotecontrol.ts index 50e08b4b..c7c67ab9 100644 --- a/src/hubs/remotecontrol.ts +++ b/src/hubs/remotecontrol.ts @@ -2,7 +2,7 @@ import { Peripheral } from "@abandonware/noble"; import { IBLEAbstraction } from "../interfaces"; -import { LPF2Hub } from "./lpf2hub"; +import { LPF2Hub } from "./generic/lpf2hub"; import * as Consts from "../consts"; @@ -18,7 +18,6 @@ const debug = Debug("remotecontrol"); */ export class RemoteControl extends LPF2Hub { - public static IsRemoteControl (peripheral: Peripheral) { return ( peripheral.advertisement && @@ -30,9 +29,18 @@ export class RemoteControl extends LPF2Hub { ); } + protected static _type: number = 4; + protected static _typeName: string = "REMOTE_CONTROL"; + protected static _portMap: {[portName: string]: number} = { + "LEFT": 0, + "RIGHT": 1, + "HUB_LED": 52, + "VOLTAGE_SENSOR": 59, + "REMOTE_CONTROL_RSSI": 60 + }; constructor (device: IBLEAbstraction) { - super(device, RemoteControl.PortMap, Consts.HubType.REMOTE_CONTROL); + super(device); debug("Discovered Powered UP Remote"); } @@ -48,15 +56,3 @@ export class RemoteControl extends LPF2Hub { } - -export namespace RemoteControl { - - export const PortMap: {[portName: string]: number} = { - "LEFT": 0, - "RIGHT": 1, - "HUB_LED": 52, - "VOLTAGE_SENSOR": 59, - "REMOTE_CONTROL_RSSI": 60 - } - -} \ No newline at end of file diff --git a/src/hubs/technicmediumhub.ts b/src/hubs/technicmediumhub.ts index 558b113b..1e8f5e4a 100644 --- a/src/hubs/technicmediumhub.ts +++ b/src/hubs/technicmediumhub.ts @@ -2,7 +2,7 @@ import { Peripheral } from "@abandonware/noble"; import { IBLEAbstraction } from "../interfaces"; -import { LPF2Hub } from "./lpf2hub"; +import { LPF2Hub } from "./generic/lpf2hub"; import * as Consts from "../consts"; @@ -18,7 +18,6 @@ const debug = Debug("technicmediumhub"); */ export class TechnicMediumHub extends LPF2Hub { - public static IsTechnicMediumHub (peripheral: Peripheral) { return ( peripheral.advertisement && @@ -30,8 +29,23 @@ export class TechnicMediumHub extends LPF2Hub { ); } + protected static _type = 6; + protected static _typeName = "TECHNIC_MEDIUM_HUB"; + protected static _portMap = { + "A": 0, + "B": 1, + "C": 2, + "D": 3, + "HUB_LED": 50, + "CURRENT_SENSOR": 59, + "VOLTAGE_SENSOR": 60, + "ACCELEROMETER": 97, + "GYRO_SENSOR": 98, + "TILT_SENSOR": 99 + }; + constructor (device: IBLEAbstraction) { - super(device, TechnicMediumHub.PortMap, Consts.HubType.TECHNIC_MEDIUM_HUB); + super(device); debug("Discovered Control+ Hub"); } @@ -107,20 +121,3 @@ export class TechnicMediumHub extends LPF2Hub { } - -export namespace TechnicMediumHub { - - export const PortMap: {[portName: string]: number} = { - "A": 0, - "B": 1, - "C": 2, - "D": 3, - "HUB_LED": 50, - "CURRENT_SENSOR": 59, - "VOLTAGE_SENSOR": 60, - "ACCELEROMETER": 97, - "GYRO_SENSOR": 98, - "TILT_SENSOR": 99 - } - -} \ No newline at end of file diff --git a/src/hubs/wedo2smarthub.ts b/src/hubs/wedo2smarthub.ts index c392c214..fa13cc36 100644 --- a/src/hubs/wedo2smarthub.ts +++ b/src/hubs/wedo2smarthub.ts @@ -2,7 +2,7 @@ import { Peripheral } from "@abandonware/noble"; import { IBLEAbstraction } from "../interfaces"; -import { BaseHub } from "./basehub"; +import { BaseHub } from "./generic/basehub"; import * as Consts from "../consts"; @@ -20,7 +20,6 @@ const debug = Debug("wedo2smarthub"); */ export class WeDo2SmartHub extends BaseHub { - public static IsWeDo2SmartHub (peripheral: Peripheral) { return ( peripheral.advertisement && @@ -29,13 +28,22 @@ export class WeDo2SmartHub extends BaseHub { ); } + protected static _type = 1; + protected static _typeName = "WEDO2_SMART_HUB"; + protected static _portMap = { + "A": 1, + "B": 2, + "CURRENT_SENSOR": 3, + "VOLTAGE_SENSOR": 4, + "PIEZO_BUZZER": 5, + "HUB_LED": 6 + }; private _lastTiltX: number = 0; private _lastTiltY: number = 0; - constructor (device: IBLEAbstraction) { - super(device, WeDo2SmartHub.PortMap, Consts.HubType.WEDO2_SMART_HUB); + super(device); debug("Discovered WeDo 2.0 Smart Hub"); } @@ -119,7 +127,7 @@ export class WeDo2SmartHub extends BaseHub { } return new Promise((resolve, reject) => { const data = Buffer.from(name, "ascii"); - // Send this twice, as sometimes the first time doesn't take + // Send this twice, as sometimes the first time doesn"t take this.send(data, Consts.BLECharacteristic.WEDO2_NAME_ID); this.send(data, Consts.BLECharacteristic.WEDO2_NAME_ID); this._name = name; @@ -227,16 +235,3 @@ export class WeDo2SmartHub extends BaseHub { } - -export namespace WeDo2SmartHub { - - export const PortMap: {[portName: string]: number} = { - "A": 1, - "B": 2, - "CURRENT_SENSOR": 3, - "VOLTAGE_SENSOR": 4, - "PIEZO_BUZZER": 5, - "HUB_LED": 6 - } - -} \ No newline at end of file diff --git a/src/index-browser.ts b/src/index-browser.ts index 284b682d..cf33ec85 100644 --- a/src/index-browser.ts +++ b/src/index-browser.ts @@ -2,76 +2,16 @@ import * as Consts from "./consts"; import { PoweredUP } from "./poweredup-browser"; -import { BaseHub } from "./hubs/basehub"; -import { DuploTrainBase } from "./hubs/duplotrainbase"; -import { Hub } from "./hubs/hub"; -import { MoveHub } from "./hubs/movehub"; -import { RemoteControl } from "./hubs/remotecontrol"; -import { TechnicMediumHub } from "./hubs/technicmediumhub"; -import { WeDo2SmartHub } from "./hubs/wedo2smarthub"; - -import { ColorDistanceSensor } from "./devices/colordistancesensor"; -import { CurrentSensor } from "./devices/currentsensor"; -import { Device } from "./devices/device"; -import { DuploTrainBaseColorSensor } from "./devices/duplotrainbasecolorsensor"; -import { DuploTrainBaseMotor } from "./devices/duplotrainbasemotor"; -import { DuploTrainBaseSpeaker } from "./devices/duplotrainbasespeaker"; -import { DuploTrainBaseSpeedometer } from "./devices/duplotrainbasespeedometer"; -import { HubLED } from "./devices/hubled"; -import { Light } from "./devices/light"; -import { MediumLinearMotor } from "./devices/mediumlinearmotor"; -import { MotionSensor } from "./devices/motionsensor"; -import { MoveHubMediumLinearMotor } from "./devices/movehubmediumlinearmotor"; -import { MoveHubTiltSensor } from "./devices/movehubtiltsensor"; -import { PiezoBuzzer } from "./devices/piezobuzzer"; -import { RemoteControlButton } from "./devices/remotecontrolbutton"; -import { SimpleMediumLinearMotor } from "./devices/simplemediumlinearmotor"; -import { TechnicLargeLinearMotor } from "./devices/techniclargelinearmotor"; -import { TechnicMediumHubAccelerometerSensor } from "./devices/technicmediumhubaccelerometersensor"; -import { TechnicMediumHubGyroSensor } from "./devices/technicmediumhubgyrosensor"; -import { TechnicMediumHubTiltSensor } from "./devices/technicmediumhubtiltsensor"; -import { TechnicXLargeLinearMotor } from "./devices/technicxlargelinearmotor"; -import { TiltSensor } from "./devices/tiltsensor"; -import { TrainMotor } from "./devices/trainmotor"; -import { VoltageSensor } from "./devices/voltagesensor"; +import * as devices from "./devices"; +import * as hubs from "./hubs"; import { isWebBluetooth } from "./utils"; // @ts-ignore window.PoweredUP = { PoweredUP, - BaseHub, - WeDo2SmartHub, - TechnicMediumHub, - Hub, - RemoteControl, - DuploTrainBase, + ...hubs, Consts, - ColorDistanceSensor, - Device, - DuploTrainBaseColorSensor, - DuploTrainBaseMotor, - DuploTrainBaseSpeaker, - DuploTrainBaseSpeedometer, - HubLED, - Light, - MediumLinearMotor, - MotionSensor, - MoveHub, - MoveHubMediumLinearMotor, - MoveHubTiltSensor, - PiezoBuzzer, - RemoteControlButton, - SimpleMediumLinearMotor, - TechnicMediumHubAccelerometerSensor, - TechnicMediumHubGyroSensor, - TechnicMediumHubTiltSensor, - TechnicLargeLinearMotor, - TechnicXLargeLinearMotor, - TiltSensor, - TrainMotor, - VoltageSensor, - CurrentSensor, + ...devices, isWebBluetooth }; - diff --git a/src/index-node.ts b/src/index-node.ts index edf4f854..f54d4e37 100644 --- a/src/index-node.ts +++ b/src/index-node.ts @@ -1,76 +1,11 @@ import * as Consts from "./consts"; +export { Consts }; import { PoweredUP } from "./poweredup-node"; -import { BaseHub } from "./hubs/basehub"; -import { DuploTrainBase } from "./hubs/duplotrainbase"; -import { Hub } from "./hubs/hub"; -import { MoveHub } from "./hubs/movehub"; -import { RemoteControl } from "./hubs/remotecontrol"; -import { TechnicMediumHub } from "./hubs/technicmediumhub"; -import { WeDo2SmartHub } from "./hubs/wedo2smarthub"; +export * from "./devices"; +export * from "./hubs"; -import { ColorDistanceSensor } from "./devices/colordistancesensor"; -import { CurrentSensor } from "./devices/currentsensor"; -import { Device } from "./devices/device"; -import { DuploTrainBaseColorSensor } from "./devices/duplotrainbasecolorsensor"; -import { DuploTrainBaseMotor } from "./devices/duplotrainbasemotor"; -import { DuploTrainBaseSpeaker } from "./devices/duplotrainbasespeaker"; -import { DuploTrainBaseSpeedometer } from "./devices/duplotrainbasespeedometer"; -import { HubLED } from "./devices/hubled"; -import { Light } from "./devices/light"; -import { MediumLinearMotor } from "./devices/mediumlinearmotor"; -import { MotionSensor } from "./devices/motionsensor"; -import { MoveHubMediumLinearMotor } from "./devices/movehubmediumlinearmotor"; -import { MoveHubTiltSensor } from "./devices/movehubtiltsensor"; -import { PiezoBuzzer } from "./devices/piezobuzzer"; -import { RemoteControlButton } from "./devices/remotecontrolbutton"; -import { SimpleMediumLinearMotor } from "./devices/simplemediumlinearmotor"; -import { TechnicLargeLinearMotor } from "./devices/techniclargelinearmotor"; -import { TechnicMediumHubAccelerometerSensor } from "./devices/technicmediumhubaccelerometersensor"; -import { TechnicMediumHubGyroSensor } from "./devices/technicmediumhubgyrosensor"; -import { TechnicMediumHubTiltSensor } from "./devices/technicmediumhubtiltsensor"; -import { TechnicXLargeLinearMotor } from "./devices/technicxlargelinearmotor"; -import { TiltSensor } from "./devices/tiltsensor"; -import { TrainMotor } from "./devices/trainmotor"; -import { VoltageSensor } from "./devices/voltagesensor"; - -import { isWebBluetooth } from "./utils"; +export { isWebBluetooth } from "./utils"; export default PoweredUP; -export { - PoweredUP, - BaseHub, - WeDo2SmartHub, - TechnicMediumHub, - Hub, - RemoteControl, - DuploTrainBase, - Consts, - ColorDistanceSensor, - Device, - DuploTrainBaseColorSensor, - DuploTrainBaseMotor, - DuploTrainBaseSpeaker, - DuploTrainBaseSpeedometer, - HubLED, - Light, - MediumLinearMotor, - MotionSensor, - MoveHub, - MoveHubMediumLinearMotor, - MoveHubTiltSensor, - PiezoBuzzer, - RemoteControlButton, - SimpleMediumLinearMotor, - TechnicMediumHubAccelerometerSensor, - TechnicMediumHubGyroSensor, - TechnicMediumHubTiltSensor, - TechnicLargeLinearMotor, - TechnicXLargeLinearMotor, - TiltSensor, - TrainMotor, - VoltageSensor, - CurrentSensor, - isWebBluetooth -}; diff --git a/src/interfaces.ts b/src/interfaces.ts index ea7111c7..5612070f 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -17,8 +17,21 @@ export interface IBLEAbstraction extends EventEmitter { } export interface IDeviceInterface extends EventEmitter { - type: Consts.HubType; + type: number; getPortNameForPortId: (portId: number) => string | undefined; send: (message: Buffer, uuid: string, callback?: () => void) => void; subscribe: (portId: number, deviceType: number, mode: number) => void; } + +export interface IDeviceMode { + input: boolean; + event?: string; + values?: { + type: Consts.ValueType, + count: number, + min?: number, + max?: number + }; + num: { [type: number]: number }; + transform?: (hubType: number, data: number[]) => number[]; +} diff --git a/src/poweredup-browser.ts b/src/poweredup-browser.ts index f9860349..f57c48dc 100644 --- a/src/poweredup-browser.ts +++ b/src/poweredup-browser.ts @@ -1,15 +1,9 @@ import { WebBLEDevice } from "./webbleabstraction"; -import { BaseHub } from "./hubs/basehub"; -import { DuploTrainBase } from "./hubs/duplotrainbase"; -import { Hub } from "./hubs/hub"; -import { MoveHub } from "./hubs/movehub"; -import { RemoteControl } from "./hubs/remotecontrol"; -import { TechnicMediumHub } from "./hubs/technicmediumhub"; -import { WeDo2SmartHub } from "./hubs/wedo2smarthub"; - import * as Consts from "./consts"; +import { BaseHub, hubs, hubType} from "./hubs"; + import { EventEmitter } from "events"; import Debug = require("debug"); @@ -127,7 +121,7 @@ export class PoweredUP extends EventEmitter { } - private _determineLPF2HubType (device: IBLEAbstraction): Promise { + private _determineLPF2HubType (device: IBLEAbstraction): Promise { return new Promise((resolve, reject) => { let buf: Buffer = Buffer.alloc(0); device.subscribeToCharacteristic(Consts.BLECharacteristic.LPF2_ALL, (data: Buffer) => { @@ -140,19 +134,19 @@ export class PoweredUP extends EventEmitter { process.nextTick(() => { switch (message[5]) { case Consts.BLEManufacturerData.REMOTE_CONTROL_ID: - resolve(Consts.HubType.REMOTE_CONTROL); + resolve(hubType.REMOTE_CONTROL); break; case Consts.BLEManufacturerData.MOVE_HUB_ID: - resolve(Consts.HubType.MOVE_HUB); + resolve(hubType.MOVE_HUB); break; case Consts.BLEManufacturerData.HUB_ID: - resolve(Consts.HubType.HUB); + resolve(hubType.HUB); break; case Consts.BLEManufacturerData.DUPLO_TRAIN_BASE_ID: - resolve(Consts.HubType.DUPLO_TRAIN_BASE); + resolve(hubType.DUPLO_TRAIN_BASE); break; case Consts.BLEManufacturerData.TECHNIC_MEDIUM_HUB: - resolve(Consts.HubType.TECHNIC_MEDIUM_HUB); + resolve(hubType.TECHNIC_MEDIUM_HUB); break; } }); @@ -170,17 +164,15 @@ export class PoweredUP extends EventEmitter { const device = new WebBLEDevice(server); - let hub: BaseHub; - - let hubType = Consts.HubType.UNKNOWN; + let type = hubType.UNKNOWN; let isLPF2Hub = false; try { await device.discoverCharacteristicsForService(Consts.BLEService.WEDO2_SMART_HUB); - hubType = Consts.HubType.WEDO2_SMART_HUB; + type = hubType.WEDO2_SMART_HUB; // tslint:disable-next-line } catch (error) {} try { - if (hubType !== Consts.HubType.WEDO2_SMART_HUB) { + if (type !== hubType.WEDO2_SMART_HUB) { await device.discoverCharacteristicsForService(Consts.BLEService.LPF2_HUB); isLPF2Hub = true; } @@ -188,32 +180,17 @@ export class PoweredUP extends EventEmitter { } catch (error) {} if (isLPF2Hub) { - hubType = await this._determineLPF2HubType(device); + type = await this._determineLPF2HubType(device); } - switch (hubType) { - case Consts.HubType.WEDO2_SMART_HUB: - hub = new WeDo2SmartHub(device); - break; - case Consts.HubType.MOVE_HUB: - hub = new MoveHub(device); - break; - case Consts.HubType.HUB: - hub = new Hub(device); - break; - case Consts.HubType.REMOTE_CONTROL: - hub = new RemoteControl(device); - break; - case Consts.HubType.DUPLO_TRAIN_BASE: - hub = new DuploTrainBase(device); - break; - case Consts.HubType.TECHNIC_MEDIUM_HUB: - hub = new TechnicMediumHub(device); - break; - default: - return; + const constructor = hubs[type]; + + if (!constructor) { + return; } + const hub = new constructor(device); + device.on("discoverComplete", () => { hub.on("connect", () => { diff --git a/src/poweredup-node.ts b/src/poweredup-node.ts index 2e95d113..6d4b8135 100644 --- a/src/poweredup-node.ts +++ b/src/poweredup-node.ts @@ -2,7 +2,7 @@ import { Peripheral } from "@abandonware/noble"; import { NobleDevice } from "./nobleabstraction"; -import { BaseHub } from "./hubs/basehub"; +import { BaseHub } from "./hubs/generic/basehub"; import { DuploTrainBase } from "./hubs/duplotrainbase"; import { Hub } from "./hubs/hub"; import { MoveHub } from "./hubs/movehub";