blinky/libs/blinkstick.js
2021-04-06 16:41:49 +02:00

259 lines
No EOL
8.5 KiB
JavaScript

// requirements
const logger = require('./logger');
// third party requirements
const blinkstick = require('blinkstick');
const async = require('async');
const hexcolor = require('hex-color-regex');
// constants
const COLORTYPE_RGB = "rgb";
const COLORTYPE_HEX = "hex";
const ANIMATION_STATE_INFINITE = 2;
const ANIMATION_STATE_INPROGRESS = 1;
const ANIMATION_STATE_FINISH = 0;
// variables
let led;
let animation = {};
function findBlinkstick() {
return new Promise(function (resolve, reject) {
led = blinkstick.findFirst();
if (led !== undefined && led !== null) {
return resolve(led);
}
return reject("could not find any blinkstick");
});
}
// turn the blinkstick off
function powerOff() {
led.turnOff();
logger.info("blinkstick powered off");
}
// parse a color object from given request arguments
function parseColor(blinkstickConfig) {
return new Promise(function (resolve, reject) {
if (blinkstickConfig.color == "random") {
resolve(color);
}
if (blinkstickConfig.color == undefined || blinkstickConfig.color.length === 0) {
reject("no hex color code given, request will be ignored");
}
let parsedColor = parseRGBColor(blinkstickConfig.color);
if (!parsedColor) {
parsedColor = parseHexColor(blinkstickConfig.color);
}
if (!parsedColor) {
return reject("could not parse given color code '" + color + "'");
}
blinkstickConfig.color = parsedColor;
resolve(blinkstickConfig);
});
}
// parse rgb color values
function parseRGBColor(colorValues) {
let color = {};
if (colorValues.indexOf(",") == -1) {
if (isRGBValue(colorValues)) {
color.type = COLORTYPE_RGB;
color.red = parseInt(colorValues);
return color;
}
} else {
const splittedValues = colorValues.split(",");
for (let index = 0; index < splittedValues.length; index++) {
const value = splittedValues[index];
if (!isRGBValue(value)) {
continue;
}
if (index == 0) {
color.red = parseInt(value);
} else if (index === 1) {
color.green = parseInt(value);
} else if (index === 2) {
color.blue = parseInt(value);
}
}
if (color && (color.red || color.green || color.blue)) {
color.type = COLORTYPE_RGB;
return color;
}
}
if (isRGBValue(colorValues.red) || isRGBValue(colorValues.green) || isRGBValue(colorValues.blue)) {
color.type = COLORTYPE_RGB;
return colorValues;
}
return undefined;
}
// check a single rgb value
function isRGBValue(value) {
return value != undefined && value.length > 0 && !isNaN(value) && value >= 0 && value <= 255;
}
// parse hex color values
function parseHexColor(colorValues) {
if (colorValues[0] !== '#') {
colorValues = '#' + colorValues;
}
if (colorValues.length === 4) {
colorValues.type = COLORTYPE_HEX;
return (colorValues[0] + colorValues[1] + colorValues[1] + colorValues[2] + colorValues[2] + colorValues[3] + colorValues[3]);
}
if (colorValues.length === 7 && hexcolor({ strict: true }).test(colorValues)) {
colorValues.type = COLORTYPE_HEX;
return colorValues;
}
return undefined;
}
// pass the options to the blinkstick accordingly
function illuminate(blinkstickConfig) {
return new Promise(function (resolve, reject) {
waitForAnimation
let color;
findBlinkstick().then(function () {
if (blinkstickConfig.color.type === COLORTYPE_HEX) {
color = blinkstickConfig.color.red;
} else {
color = blinkstickConfig.color.red + ", " + blinkstickConfig.color.green + ", " + blinkstickConfig.color.blue;
}
switch (blinkstickConfig.mode) {
case "morph":
morph(blinkstickConfig, color, resolve, reject);
break;
case "pulse":
startPulsing(blinkstickConfig, color, resolve, reject);
break;
default:
setColor(blinkstickConfig, color, resolve, reject);
break;
}
})
.catch(reject);
});
}
// set a static color
function setColor(blinkstickConfig, color, resolve, reject) {
stopAnimation()
.then(function () {
setAnimationProperties(blinkstickConfig);
led.setColor(blinkstickConfig.color.red, blinkstickConfig.color.green, blinkstickConfig.color.blue, function (err) {
clearAnimationProperties();
logger.debug("setting color to '" + color + "'...");
if (err) {
return reject("error setting color '" + color + "' (" + err + ")");
}
return resolve("set color to '" + color + "'");
});
});
}
// morph to a color
function morph(blinkstickConfig, color, resolve, reject) {
stopAnimation()
.then(function () {
setAnimationProperties(blinkstickConfig);
led.morph(blinkstickConfig.color.red, blinkstickConfig.color.green, blinkstickConfig.color.blue, blinkstickConfig.options, function (err) {
clearAnimationProperties();
logger.debug("morphing color to '" + color + "'...");
if (err) {
return reject("error morphing color to '" + color + "' (" + err + ")");
}
return resolve("morphed color to '" + color + "'");
});
});
}
// start pulsing
function startPulsing(blinkstickConfig, color, resolve, reject) {
if (animation.state == ANIMATION_STATE_FINISH || (blinkstickConfig.options.pulse.max > 0 && (blinkstickConfig.options.pulse.done && blinkstickConfig.options.pulse.done < blinkstickConfig.options.pulse.max))) {
clearAnimationProperties();
return resolve("finished pulsing color '" + color + "'");
}
if (animation.id && animation.id != blinkstickConfig.id) {
stopAnimation()
.then(logger.info)
.then(function () {
startPulsing(blinkstickConfig, color, resolve, reject)
})
.catch(logger.error);
return;
}
setAnimationProperties(blinkstickConfig);
led.pulse(blinkstickConfig.color.red, blinkstickConfig.color.green, blinkstickConfig.color.blue, blinkstickConfig.options, function (err) {
if (err) {
clearAnimationProperties();
return reject("error pulsing color '" + color + "' (" + err + ")");
}
blinkstickConfig.options.pulse.done++;
logger.debug("pulsed color '" + color + "' " + blinkstickConfig.options.pulse.done + "/" + blinkstickConfig.options.pulse.max + " times");
startPulsing(blinkstickConfig, color, resolve, reject);
});
}
// set properties for the current animation
function setAnimationProperties(blinkstickConfig) {
if (animation.id == blinkstickConfig.id) {
return;
}
led.animationsEnabled = true;
animation.id = blinkstickConfig.id;
if (blinkstickConfig.options.pulse && blinkstickConfig.options.pulse.max === 0) {
animation.state = ANIMATION_STATE_INFINITE;
} else {
animation.state = ANIMATION_STATE_INPROGRESS;
}
}
// clear properties for the current animation
function clearAnimationProperties() {
led.stop();
animation = {};
}
// stop the current animation
function stopAnimation() {
return new Promise(function (resolve, reject) {
if (!isAnimationInProgress()) {
return resolve();
}
animation.state = ANIMATION_STATE_FINISH;
waitForAnimation(Date.now(), resolve, reject);
});
}
// wait for current animation
function waitForAnimation(timestamp, resolve, reject) {
if (!isAnimationInProgress()) {
return resolve();
}
setTimeout(function () {
waitForAnimation(timestamp, resolve, reject);
}, 100);
}
// is currently an animation in progress
function isAnimationInProgress() {
return animation != undefined && animation.state != undefined;
}
// is currently an infinite animation in progress
function isInfiniteAnimationInProgress() {
return isAnimationInProgress() && animation.state == ANIMATION_STATE_INFINITE;
}
// exports
module.exports = {
parseColor,
illuminate,
isAnimationInProgress,
isInfiniteAnimationInProgress,
stopAnimation,
powerOff
};