blinkstick-ts
Version:
BlinkStick TypeScript implementation
507 lines (506 loc) • 16.1 kB
JavaScript
"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;
}