UNPKG

blinkstick-ts

Version:
507 lines (506 loc) 16.1 kB
"use strict"; /** * Provides access to BlinkStick devices * * @module blinkstick */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BlinkStick = void 0; const usb = __importStar(require("node-hid")); const blinkstick_channel_1 = require("./blinkstick-channel"); const color_1 = require("./color"); class BlinkStick { /** * Initialize new BlinkStick device * * @class BlinkStick * @constructor * @param {Object} device The USB device as returned from "usb" package. * @param {String} [serialNumber] Serial number of the device. Used only in Windows. * @param {String} [manufacturer] Manufacturer of the device. Used only in Windows. * @param {String} [product] Product name of the device. Used only in Windows. */ constructor(device, serialNumber, manufacturer, product) { this.device = new usb.HID(device); this.serial = serialNumber; this.manufacturer = manufacturer; this.product = product; this.inverse = false; const major = this.versionMajor; const minor = this.versionMinor; this.requiresSoftwareColorPatch = !!(major && minor && major == 1 && minor >= 1 && minor <= 3); } /** * Close BlinkStick device and stop all animations * * @method close */ close() { try { this.device.close(); } catch (ex) { console.log(ex); } } ; /** * Get the major version from serial number * * @method getVersionMajor * @return {Number} Major version number from serial */ get versionMajor() { if (!this.serial) { return undefined; } return parseInt(this.serial.substring(this.serial.length - 3, this.serial.length - 2)); } ; /** * Get the minor version from serial number * * @method getVersionMinor * @return {Number} Minor version number from serial */ get versionMinor() { if (!this.serial) { return undefined; } return parseInt(this.serial.substring(this.serial.length - 1, this.serial.length)); } ; setRandomColor() { this.setColor(new color_1.Color(randomIntInc(0, 255), randomIntInc(0, 255), randomIntInc(0, 255))); } sendColorInternal(color, channel, index) { if (channel === 0 && index === 0) { this.setFeatureReport(1, [1, color.red, color.green, color.blue]); } else { this.setFeatureReport(5, [5, channel, index, color.red, color.green, color.blue]); } } /** * Set the color of LEDs * * @example * //Available overloads * setColor(red, green, blue, [options], [callback]); // use [0..255] ranges for intensity * * setColor(color, [options], [callback]); // use '#rrggbb' format * * setColor(color_name, [options], [callback]); // use 'random', 'red', 'green', 'yellow' and other CSS supported names * * @method setColor * @param {Color} index The index to set * @param {Color} color The color to set * @param {Object} [options] additional options {"channel": 0, "index": 0}. Channel is represented as 0=R, 1=G, 2=B * @param {Function} [callback] Callback, called when complete. */ setColor(color, index = 0, channel = 0) { if (this.inverse) { color = color.inverse(); } if (this.requiresSoftwareColorPatch) { var current = this.getColor(); if (color == current) { current = new color_1.Color(current.red > 0 ? current.red - 1 : 0, current.green > 0 ? current.green - 1 : 0, current.blue); this.sendColorInternal(current, channel, index); this.sendColorInternal(color, channel, index); } else { this.sendColorInternal(color, channel, index); } } else { this.sendColorInternal(color, channel, index); } } ; /** * Set mode for BlinkStick Pro * * - 0 = Normal * - 1 = Inverse * - 2 = WS2812 * * You can read more about BlinkStick modes by following this link: * * http://www.blinkstick.com/help/tutorials/blinkstick-pro-modes * * @method setMode * @param {Number} mode Set the desired mode for BlinkStick Pro */ setMode(mode) { this.setFeatureReport(0x0004, [4, mode]); } ; /** * Get mode for BlinkStick Pro * * - 0 = Normal * - 1 = Inverse * - 2 = WS2812 * * You can read more about BlinkStick modes by following this link: * * http://www.blinkstick.com/help/tutorials/blinkstick-pro-modes * * Usage: * * @example * getMode(function(err, data) { * console.log(data); * }); * * @method getMode */ getMode() { return __awaiter(this, void 0, void 0, function* () { const buffer = yield this.getFeatureReport(4, 33); return buffer ? buffer[1] : undefined; }); } ; /** * Get the current color visible on BlinkStick * * Function supports the following overloads: * * @example * //Available overloads * getColor(callback); //index defaults to 0 * * getColor(index, callback); * * @example * getColor(0, function(err, r, g, b) { * console.log(r, g, b); * }); * * @method getColor * @param {Number=0} index The index of the LED * @param {Function} callback Callback to which to pass the color values. * @return {Number, Number, Number} Callback returns three numbers: R, G and B [0..255]. */ getColor(index = 0, channel = 0) { if (index === 0) { // TODO unify with get colors const buffer = this.getFeatureReport(1, 3); return new color_1.Color(buffer[1], buffer[2], buffer[3]); } else { const colors = this.getColors(1, index, channel); return colors[0]; } } ; /** * Get the current color frame on BlinkStick Pro * * @method getColors * @param {Number} index Where to start * @param {Number} count How many LEDs should return * @return {Array} Callback returns an array of LED data in the following format: [g0, r0, b0, g1, r1, b1...] */ getColors(count, index = 0, channel = 0) { const params = this.determineReportId(index + count); var buffer = this.getFeatureReport(params.reportId, params.maxLeds * 3 + 2); buffer = buffer.slice(2, buffer.length - 1); // cut of header buffer = buffer.slice(index * 3, buffer.length - 1); // cut off until index const result = []; for (var i = 0; i < count; i++) { result.push(new color_1.Color(buffer[i * 3 + 1], buffer[i * 3], buffer[i * 3 + 2])); } return result; } ; /** * Set the color frame on BlinkStick Pro * * @example * var data = [255, 0, 0, 0, 255, 0]; * * setColors(0, data, function(err) { * }); * * @method setColors * @param {Number} channel Channel is represented as 0=R, 1=G, 2=B * @param {Array} data LED data in the following format: [g0, r0, b0, g1, r1, b1...] * @param {Function} callback Callback when the operation completes */ setColors(data, channel = 0) { const params = this.determineReportId(data.length); const report = [params.reportId, channel]; for (var j = 0; j < params.maxLeds; j++) { if (j >= data.length) { report.push(0, 0, 0); } else { report.push(data[j].red, data[j].green, data[j].blue); } } this.setFeatureReport(params.reportId, report); } ; getChannel(channel) { return new blinkstick_channel_1.BlinkStickChannel(this, channel); } /** * Get an infoblock from a device. * * @private * @static * @method getInfoBlock * @param {BlinkStick} device Device from which to get the value. * @param {Number} location Address to seek the data. * @param {Function} callback Callback to which to pass the value. */ getInfoBlock(location) { const buffer = this.device.getFeatureReport(location, 33); var result = '', i, l; for (i = 1, l = buffer.length; i < l; i++) { if (i === 0) break; result += String.fromCharCode(buffer[i]); } return result; } /** * Get the infoblock1 of the device. * This is a 32 byte array that can contain any data. It's supposed to * hold the "Name" of the device making it easier to identify rather than * a serial number. * * Usage: * * @example * getInfoBlock1(function(err, data) { * console.log(data); * }); * * @method getInfoBlock1 * @param {Function} callback Callback to which to pass the value. */ getInfoBlock1() { return this.getInfoBlock(0x0002); } ; /** * Get the infoblock2 of the device. * This is a 32 byte array that can contain any data. * * Usage: * * @example * getInfoBlock2(function(err, data) { * console.log(data); * }); * * @method getInfoBlock2 * @param {Function} callback Callback to which to pass the value. */ getInfoBlock2() { return this.getInfoBlock(0x0003); } ; /** * Sets an infoblock on a device. * * @private * @static * @method setInfoBlock * @param {BlinkStick} device Device on which to set the value. * @param {Number} location Address to seek the data. * @param {String} data The value to push to the device. Should be <= 32 chars. * @param {Function} callback Callback to which to pass the value. */ setInfoBlock(location, data) { const l = Math.min(data.length, 33); const buffer = Buffer.alloc(33); buffer[0] = 0; for (var i = 0; i < l; i++) { buffer[i + 1] = data.charCodeAt(i); } for (var i = l; i < 33; i++) { buffer[i + 1] = 0; } this.setFeatureReport(location, buffer); } /** * Sets the infoblock1 with specified string. * It fills the rest of bytes with zeros. * * Usage: * * @example * setInfoBlock1("abcdefg", function(err) { * }); * * @method setInfoBlock1 * @param {String} data Data value for InfoBlock * @param {Function} callback Callback when the operation completes */ setInfoBlock1(data) { this.setInfoBlock(0x0002, data); } ; /** * Sets the infoblock2 with specified string. * It fills the rest of bytes with zeros. * * Usage: * * @example * setInfoBlock2("abcdefg", function(err) { * }); * * @method setInfoBlock2 * @param {String} data Data value for InfoBlock * @param {Function} callback Callback when the operation completes */ setInfoBlock2(data) { this.setInfoBlock(0x0003, data); } ; /** * Turns the LED off. * * @method turnOff */ turnOff() { this.setColor(new color_1.Color(0, 0, 0)); } ; /** * Determines report ID and number of LEDs for the report * * @private * @method determineReportId * @return {object} data.reportId and data.ledCount */ determineReportId(ledCount) { if (ledCount < 8) { return { reportId: 6, maxLeds: 8 }; } else if (ledCount < 16) { return { reportId: 7, maxLeds: 16 }; } else if (ledCount < 32) { return { reportId: 8, maxLeds: 32 }; } else { return { reportId: 9, maxLeds: 64 }; } } /** * Set feature report to the device. * * @method setFeatureReport * @param {Number} reportId Report ID to receive * @param {Array} data Data to send to the device * @param {Function} callback Function called when report sent */ setFeatureReport(reportId, data) { var error; for (var tries = 0; tries < 5; tries++) { try { return this.device.sendFeatureReport(data); } catch (ex) { if (typeof (error) === 'undefined') { //Store only the first error error = ex; } } } ; throw Error(error); } ; /** * Get feature report from the device. * * @method getFeatureReport * @param {Number} reportId Report ID to receive * @param {Number} length Expected length of the report */ getFeatureReport(reportId, length) { var error; for (var tries = 0; tries < 5; tries++) { try { return this.device.getFeatureReport(reportId, length); } catch (ex) { console.error(ex); if (typeof (error) === 'undefined') { //Store only the first error error = ex; } } } ; throw Error(error); } ; } exports.BlinkStick = BlinkStick; /** * Generate random integer number within a range. * * @private * @static * @method randomIntInc * @param {Number} low the low value of the number * @param {Number} high the high value of the number * @return {Number} Random number in the range of [low..high] inclusive of low and high */ function randomIntInc(low, high) { return Math.floor(Math.random() * (high - low + 1) + low); } /** * Get default value from options * * @private * @static * @method opt * @param {Object} options Option object to operate on * @param {String} name The name of the parameter * @param {Object} defaultValue Default value if name is not found in option object */ function opt(options, name, defaultValue) { return options && name in options ? options[name] : defaultValue; }