@shuttle-lib/core
Version:
NPM package to interact with the Shuttle devices
150 lines • 5.77 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Shuttle = void 0;
const eventemitter3_1 = require("eventemitter3");
const products_js_1 = require("./products.js");
const lib_js_1 = require("./lib.js");
class Shuttle extends eventemitter3_1.EventEmitter {
/** Vendor ids for the Shuttle devices */
static get vendorIds() {
return products_js_1.VENDOR_IDS;
}
constructor(_device, _deviceInfo, _devicePath) {
super();
this._device = _device;
this._deviceInfo = _deviceInfo;
this._devicePath = _devicePath;
this._buttonStates = new Map();
this._shuttleState = 0;
this._jogState = 0;
this._initialized = false;
this._disconnected = false;
this.product = this._setupDevice(_deviceInfo);
}
_setupDevice(deviceInfo) {
const findProduct = () => {
for (const product of Object.values(products_js_1.PRODUCTS)) {
if (product.vendorId === deviceInfo.vendorId &&
product.productId === deviceInfo.productId &&
product.interface === deviceInfo.interface) {
return {
product,
vendorId: deviceInfo.vendorId,
productId: deviceInfo.productId,
interface: deviceInfo.interface,
};
}
}
// else:
throw new Error(`Unknown/Unsupported Shuttle device: "${deviceInfo.product}" (vendorId: "${deviceInfo.vendorId}", productId: "${deviceInfo.productId}", interface: "${deviceInfo.interface}").\nPlease report this as an issue on our github page!`);
};
const found = findProduct();
for (let i = 0; i < found.product.buttonBits.length; i++) {
if (!this._buttonStates.has(i))
this._buttonStates.set(i, false);
}
this._device.on('data', (data) => {
const dataView = (0, lib_js_1.uint8ArrayToDataView)(data);
const shuttle = dataView.getInt8(0);
if (shuttle !== this._shuttleState) {
this._shuttleState = shuttle;
this.emit('shuttle', shuttle);
}
const jog = dataView.getUint8(1);
if (jog !== this._jogState) {
let delta = jog - this._jogState;
if (this._jogState > 250 && jog < 5) {
delta += 256; // fix overflow
}
else if (this._jogState < 5 && jog > 250) {
delta -= 256; // fix underflow
}
this._jogState = jog;
this.emit('jog', delta, jog);
}
const buttons = dataView.getUint16(3, true);
for (let i = 0; i < found.product.buttonBits.length; i++) {
const button = Boolean((0, lib_js_1.getBit)(buttons, found.product.buttonBits[i]));
if (button !== this._buttonStates.get(i)) {
this._buttonStates.set(i, button);
if (button)
this.emit('down', i);
else
this.emit('up', i);
}
}
});
this._device.on('error', (err) => {
if ((err + '').match(/could not read from/)) {
// The device has been disconnected
this._triggerHandleDeviceDisconnected();
}
else if ((err + '').match(/WebHID disconnected/)) {
this._triggerHandleDeviceDisconnected();
}
else {
this.emit('error', err);
}
});
return {
...found.product,
productId: found.productId,
vendorId: found.vendorId,
};
}
/** Initialize the device. This ensures that the essential information from the device about its state has been received. */
async init() {
// Nothing to do here, but keeping this as a placeholder for future use.
this._initialized = true;
}
/** Closes the device. Subsequent commands will raise errors. */
async close() {
await this._handleDeviceDisconnected();
}
/** Various information about the device and its capabilities */
get info() {
this.ensureInitialized();
return (0, lib_js_1.literal)({
productModelId: this.product.productModelId,
name: this.product.name,
vendorId: this.product.vendorId,
productId: this.product.productId,
interface: this.product.interface,
});
}
/**
* Returns an object with current Button states
*/
getButtons() {
return new Map(this._buttonStates); // Make a copy
}
_triggerHandleDeviceDisconnected() {
this._handleDeviceDisconnected().catch((error) => {
this.emit('error', error);
});
}
/** (Internal function) Called when there has been detected that the device has been disconnected */
async _handleDeviceDisconnected() {
if (!this._disconnected) {
this._disconnected = true;
await this._device.close();
this.emit('disconnected');
}
}
get hidDevice() {
return this._device;
}
get deviceInfo() {
return this._deviceInfo;
}
get devicePath() {
return this._devicePath;
}
/** Check that the .init() function has run, throw otherwise */
ensureInitialized() {
if (!this._initialized)
throw new Error('Shuttle.init() must be run first!');
}
}
exports.Shuttle = Shuttle;
//# sourceMappingURL=Shuttle.js.map