reworked almost the whole project - everything should work now as expected

This commit is contained in:
Daniel Sommer 2022-02-23 17:08:41 +01:00
parent eac3aa5e5e
commit 61c43fdd9b
6 changed files with 513 additions and 290 deletions

View file

@ -12,11 +12,13 @@
"description": "show this page"
},
"post": {
"mode": {
"available": "set, morph, pulse",
"default": "set",
"description": "specifies the color change mode"
},
"endpoints": [
"/set",
"/morph",
"/blink",
"/pulse",
"/poweroff"
],
"color": {
"available": "random, hex color codes (#ffffff), rgb color codes (255, 255, 255)",
"default": "random",
@ -24,8 +26,8 @@
},
"index": {
"available": "number values",
"default": "undefined",
"description": "specifies the led index"
"default": "all",
"description": "specifies the led index / defaults to all leds"
},
"duration": {
"available": "number values",
@ -35,13 +37,19 @@
"steps": {
"available": "number values",
"default": "[duration] / 10",
"description": "specifies the amount of steps for the color change"
"description": "specifies the number of steps for the color change"
},
"pulses": {
"restrictions": "pulse",
"repeats": {
"restrictions": "blink, pulse",
"available": "number values",
"default": "0 (infinite)",
"description": "specifies the number of pulses"
"description": "specifies the number of blinks/pulses"
},
"delay": {
"restrictions": "blink",
"available": "number values",
"default": "500",
"description": "delay between blinks in milliseconds "
}
}
}

View file

@ -1,109 +1,123 @@
// requirements
const logger = require('./logger');
const config = require('../config.json');
const util = require('util');
// third party requirements
const blinkstick = require('blinkstick');
const hexcolor = require('hex-color-regex');
// constants
// const LEDS = [0, 1, 2, 3, 4, 5, 6, 7];
const LEDS_ALL = 'index_all';
const RANDOM = 'random';
const ANIMATION_STATE_INPROGRESS = 1;
const ANIMATION_STATE_FINISH = 0;
const LED_ANIMATED = 0;
const LEDS_ALL = 'all';
const MODE_SET = 'set';
const MODE_MORPH = 'morph';
const MODE_BLINK = 'blink';
const MODE_PULSE = 'pulse';
const MODE_POWEROFF = 'poweroff';
// variables
let led;
const LEDS = new Map();
let stop = false;
// find a connected blinkstick
function findBlinkstick() {
return new Promise((resolve, reject) => {
led = blinkstick.findFirst();
if (led !== undefined && led !== null) {
return resolve(led);
let led = blinkstick.findFirst();
if (led === undefined) {
reject('could not find any blinkstick');
}
return reject('could not find any blinkstick');
resolve(led);
});
}
// light it up
async function illuminate(blinkstickConfig) {
led = await findBlinkstick();
let indices = getIndices(blinkstickConfig);
for (let index = 0; index < indices.length; index++) {
// simple animation (set the color / morph to color)
async function simple(config) {
try {
await setLedState(indices[index], ANIMATION_STATE_INPROGRESS);
await singleAnimation(JSON.parse(JSON.stringify(blinkstickConfig)), indices[index]);
blinkstickConfig.options.pulse++;
if (maxPulsesReached(blinkstickConfig)) {
return;
}
} catch (err) {
logger.error(err);
}
}
if (!maxPulsesReached(blinkstickConfig)) {
return illuminate(blinkstickConfig);
}
}
function maxPulsesReached(blinkstickConfig) {
if (blinkstickConfig.mode !== MODE_PULSE) {
return true;
}
return (blinkstickConfig.options.pulse.max === 0 || blinkstickConfig.options.pulse.done > blinkstickConfig.options.pulse.max);
}
// turn the blinkstick or specified led off
async function powerOff(index) {
led = await findBlinkstick();
// check for NaN
if (index !== index) {
index = LEDS_ALL;
}
let config = {color: '#000000', mode: MODE_POWEROFF, options: {index: index}};
await forceStop();
config.timestamp = new Date().getTime();
let indices = getIndices(config);
for (let index = 0; index < indices.length; index++) {
try {
await setLedState(indices[index], LED_ANIMATED);
await singleAnimation(JSON.parse(JSON.stringify(config)), indices[index]);
await clearLedState(indices[index]);
}
return {
status: 'ok',
color: config.color,
indices: indices,
time: (new Date().getTime() - config.timestamp) + 'ms'
};
} catch (err) {
logger.error(err);
return err;
}
}
// complex animation (pulse / blink)
async function complex(config) {
try {
if (config.timestamp === undefined) {
await powerOff({id: Math.random(), mode: MODE_POWEROFF, color: '#000000', options: {index: LEDS_ALL}});
config.timestamp = new Date().getTime();
}
if (index !== LEDS_ALL) {
logger.info('led \'' + index + '\' powered off');
return;
let indices = getIndices(config);
for (let index = 0; index < indices.length; index++) {
if (shouldLedFinish(config)) {
return {status: 'ok', time: (new Date().getTime() - config.timestamp) + 'ms'};
}
led.stop();
led.turnOff();
await setLedState(indices[index], LED_ANIMATED);
await singleAnimation(JSON.parse(JSON.stringify(config)), indices[index]);
await clearLedState(indices[index]);
config.repetitions.done++;
}
return complex(config);
} catch (err) {
logger.error(err);
return err;
}
}
// power the blinkstick (or just a specific led) off
async function powerOff(config) {
try {
await forceStop();
config.timestamp = new Date().getTime();
let indices = getIndices(config);
for (let index = 0; index < indices.length; index++) {
await singleAnimation(JSON.parse(JSON.stringify(config)), indices[index]);
logger.info('led \'' + indices[index] + '\' powered off');
}
if (config.options.index === LEDS_ALL) {
const blinkstick = await findBlinkstick();
blinkstick.stop();
blinkstick.turnOff();
logger.info('blinkstick powered off');
}
return {status: 'ok', indices: indices, time: (new Date().getTime() - config.timestamp) + 'ms'};
} catch (err) {
return err;
}
}
// animations
function singleAnimation(config, index) {
return new Promise((resolve, reject) => {
async function singleAnimation(config, index) {
try {
config.options.index = index;
const blinkstick = await findBlinkstick();
return await new Promise((resolve, reject) => {
logger.debug('changing color of led \'' + config.options.index + '\' to \'' + config.color + '\' (mode: ' + config.mode + ')...');
switch (config.mode) {
case MODE_MORPH:
led.morph(config.color, config.options, callback);
blinkstick.morph(config.color, config.options, callback);
break;
case MODE_BLINK:
blinkstick.blink(config.color, config.options, callback);
break;
case MODE_PULSE:
led.pulse(config.color, config.options, callback);
blinkstick.pulse(config.color, config.options, callback);
break;
default:
led.setColor(config.color, config.options, callback);
blinkstick.setColor(config.color, config.options, callback);
break;
}
function callback(err) {
clearLedState(config.options.index);
if (err) {
reject('changing color of led \'' + config.options.index + '\' to \'' + config.color + '\' encountered an error > ' + err);
}
@ -111,45 +125,31 @@ function singleAnimation(config, index) {
resolve();
}
});
} catch (err) {
return err;
}
}
// TODO: IMPLEMENT FUNCTION
function continousAnimation(config, index) {
}
// start pulsing
function startPulsing(blinkstickConfig) {
async function forceStop() {
return new Promise((resolve, reject) => {
let animation = getAnimation(blinkstickConfig);
if (animation?.state === ANIMATION_STATE_FINISH || (blinkstickConfig.options.pulse.max > 0 && animation.done === blinkstickConfig.options.pulse.max)) {
clearAnimationProperties(blinkstickConfig);
return resolve('finished pulsing \'' + blinkstickConfig.color + '\'');
}
let currentAnimation = getAnimationByIndex(blinkstickConfig?.options?.index);
if (currentAnimation !== undefined && currentAnimation.id !== blinkstickConfig.id) {
stopAnimation(currentAnimation)
.then(logger.info)
.then(() => {
startPulsing(blinkstickConfig, resolve, reject)
})
.catch(logger.error);
return;
}
animation = setAnimationProperties(blinkstickConfig);
led.pulse(blinkstickConfig.color, blinkstickConfig.options, (err) => {
if (err) {
clearAnimationProperties(blinkstickConfig);
return reject('error pulsing ' + msg + ' > ' + err);
}
animation.done++;
logger.debug('pulsed ' + msg + ' ' + animation.done + '/' + blinkstickConfig.options.pulse.max + ' times');
startPulsing(blinkstickConfig)
.catch(reject);
stop = true;
waitForAllAnimationsEnd(() => {
stop = false;
resolve();
});
});
}
function shouldLedFinish(config) {
if (stop) {
return true;
}
if (config.mode === MODE_BLINK && config.mode === MODE_PULSE) {
return config.repetitions.max !== 0 && config.repetitions.done >= config.repetitions.max
}
return false;
}
// led / index helper functions
function getIndices(blinkstickConfig) {
if (blinkstickConfig.options.index === LEDS_ALL) {
@ -164,7 +164,10 @@ async function setLedState(index, state) {
}
function clearLedState(index) {
return new Promise((resolve, reject) => {
LEDS.delete(index);
resolve();
});
}
function isLedAnimated(index) {
@ -188,71 +191,35 @@ function waitForAnimationEnd(index, callback) {
}, 100);
}
// color / parser functions
function parseColor(value) {
if (!value || value === RANDOM) {
return RANDOM;
function waitForAllAnimationsEnd(callback) {
if (LEDS.size === 0) {
return callback();
}
let parsedColor = parseRGBColor(value);
if (!parsedColor) {
parsedColor = parseHexColor(value);
}
return parsedColor || function () {
logger.warn('could not parse color value \'' + value + '\', defaulting to \'' + config.api.post.color.default + '\'');
return config.api.post.color.default;
}();
setTimeout(() => {
waitForAllAnimationsEnd(callback);
}, 100);
}
function parseRGBColor(value) {
if (value.indexOf(',') === -1 && isRGBValue(value)) {
return convertRGBToHex(parseInt(value) || 0, 0, 0);
} else {
const splittedValues = value.split(',');
let color = {};
for (let index = 0; index < splittedValues.length; index++) {
const value = splittedValues[index];
if (index === 0) {
color.red = parseInt(value) || 0;
} else if (index === 1) {
color.green = parseInt(value) || 0;
} else if (index === 2) {
color.blue = parseInt(value) || 0;
function isInfiniteAnimation(config) {
if (config.mode !== MODE_BLINK && config.mode !== MODE_PULSE) {
return false;
}
if (config.options.index === LEDS_ALL) {
return config.repetitions.max === 0;
}
if (isRGBValue(color.red) && isRGBValue(color.green) && isRGBValue(color.blue)) {
return convertRGBToHex(color.red, color.green, color.blue);
}
}
}
function isRGBValue(value) {
return value !== undefined && !isNaN(value) && value >= 0 && value <= 255;
}
function convertRGBToHex(red, green, blue) {
return '#' + ((1 << 24) + (red << 16) + (green << 8) + blue).toString(16).slice(1);
}
function parseHexColor(value) {
if (value[0] !== '#') {
value = '#' + value;
}
if (value.length === 4) {
value = (value[0] + value[1] + value[1] + value[2] + value[2] + value[3] + value[3]);
}
if (hexcolor({strict: true}).test(value)) {
return value;
}
return config.options.repeats === 0;
}
// exports
module.exports = {
parseColor,
illuminate,
simple,
complex,
powerOff,
isInfiniteAnimation,
LEDS_ALL,
MODE_SET,
MODE_MORPH,
MODE_BLINK,
MODE_PULSE,
MODE_POWEROFF
};

66
libs/index.js Normal file
View file

@ -0,0 +1,66 @@
const packageJSON = require('../package.json');
const config = require('../config.json');
function get() {
let html =
'<html>' +
'<head>' +
'<title>' + packageJSON.name + ' ' + packageJSON.version + '</title>' +
'<style>' +
'body { background: #2a2a2a; color: #f1f1f1; margin: 1.25% }' +
'th, td { border: 1px solid #919191; padding: 6px; text-align: left }' +
'</style>' +
'</head>' +
'<body>' +
'<div>' +
'<h1>' + packageJSON.name + ' ' + packageJSON.version + '</h1>' +
'</div>' +
'<hr>';
html +=
'<div>' +
'<h2>get:</h2>' +
'<p>' + config.api.get.description + '</p>' +
'</div>';
html +=
'<div>' +
'<h2>post:</h2>' +
'<h3>endpoints: </>';
for (let index = 0; index < config.api.post.endpoints.length; index++) {
if (index > 0) {
html += ', ';
}
html += config.api.post.endpoints[index];
}
html += '</h3>' +
'<table style=\'width:100%\'>' +
'<th>argument</th>' +
'<th>available values</th>' +
'<th>default</th>' +
'<th>restrictions</th>' +
'<th>description</th>';
Object.keys(config.api.post).forEach((argument) => {
if (argument === 'endpoints') {
return;
}
let restrictions = config.api.post[argument].restrictions || '';
html +=
'<tr>' +
'<td>' + argument + '</td>' +
'<td>' + config.api.post[argument].available + '</td>' +
'<td>' + config.api.post[argument].default + '</td>' +
'<td>' + restrictions + '</td>' +
'<td>' + config.api.post[argument].description + '</td>' +
'</tr>';
});
html +=
'</table>' +
'</div>' +
'</body>' +
'</html>';
return html;
}
module.exports = {
get
};

View file

@ -52,7 +52,7 @@ function getTimestamp() {
if (global.config !== undefined && global.config.log !== undefined && global.config.log.format !== undefined) {
return global.config.log.timestamp;
}
return "DD.MM.YYYY HH:mm:ss:SS";
return 'DD.MM.YYYY HH:mm:ss:SS';
}
// log a http request

188
libs/parser.js Normal file
View file

@ -0,0 +1,188 @@
const logger = require('./logger.js');
const hexcolor = require('hex-color-regex');
const LEDS_ALL = require('./blinkstick.js').LEDS_ALL;
const MODE_SET = require('./blinkstick.js').MODE_SET;
const MODE_MORPH = require('./blinkstick.js').MODE_MORPH;
const MODE_BLINK = require('./blinkstick.js').MODE_BLINK;
const MODE_PULSE = require('./blinkstick.js').MODE_PULSE;
const MODE_POWEROFF = require('./blinkstick.js').MODE_POWEROFF;
const DURATION_DEFAULT = require('../config.json').api.post.duration.default || 1000;
const DELAY_DEFAULT = require('../config.json').api.post.delay.default || 500;
const COLOR_RANDOM = 'random';
// parse a http post and return an object with sane defaults
function parseRequest(requestBody, mode) {
if (mode === undefined) {
mode = MODE_SET;
}
let config = {
'id': Math.random(),
'mode': mode,
'options': {
'index': parseIndex(requestBody.index),
}
};
if (config.mode === MODE_POWEROFF) {
config.color = '#000000';
return config;
} else {
config.color = parseColor(requestBody.color);
}
switch (config.mode) {
case MODE_MORPH:
config.options.duration = parseDuration(requestBody.duration, config.options.index);
config.options.steps = parseSteps(requestBody.steps, config.options.duration);
break;
case MODE_BLINK:
config.options.repeats = parseRepeats(requestBody.repeats, config.options.index);
config.repetitions = parseRepetitions(requestBody.repeats, config.options.index);
config.options.delay = parseDelay(requestBody.delay);
break;
case MODE_PULSE:
config.options.duration = parseDuration(requestBody.duration, config.options.index, MODE_PULSE);
config.options.steps = parseSteps(requestBody.steps, config.options.duration);
config.options.repeats = parseRepeats(requestBody.repeats, config.options.index);
config.repetitions = parseRepetitions(requestBody.repeats, config.options.index);
break;
}
return config;
}
// parse the index
function parseIndex(index) {
if (index === undefined) {
return LEDS_ALL;
}
index = parseInt(index);
if (index !== index) {
return LEDS_ALL;
}
if (index < 0) {
index = 0;
} else if (index > 7) {
index = 7;
}
return index;
}
// parse the duration
function parseDuration(duration, index, mode) {
if (duration === undefined) {
duration = DURATION_DEFAULT;
}
if (index === LEDS_ALL && mode !== MODE_PULSE) {
duration = duration / 8;
}
if (duration < 100) {
duration = 100;
}
return duration;
}
// parse the steps
function parseSteps(steps, duration) {
if (duration === undefined) {
duration = DURATION_DEFAULT;
}
if (steps === undefined || steps === 0) {
steps = duration / 10;
}
return steps;
}
// parse the repeats
function parseRepeats(repeats, index) {
if (index === LEDS_ALL) {
return 1;
}
repeats = parseInt(repeats);
if (repeats !== repeats) {
return 0;
}
return repeats;
}
// parse the repetitions
function parseRepetitions(repeats, index) {
if (index !== LEDS_ALL) {
return {done: 0, max: 1};
}
repeats = parseInt(repeats);
if (repeats !== repeats) {
repeats = 0;
}
return {done: 0, max: repeats};
}
// parse the delay
function parseDelay(delay) {
if (delay === undefined) {
delay = DELAY_DEFAULT;
}
return delay;
}
// color / parser functions
function parseColor(value) {
if (value === undefined || value === COLOR_RANDOM) {
return COLOR_RANDOM;
}
let parsedColor = parseRGBColor(value);
if (!parsedColor) {
parsedColor = parseHexColor(value);
}
return parsedColor || function () {
logger.warn('could not parse color value \'' + value + '\', defaulting to \'' + COLOR_RANDOM + '\'');
return COLOR_RANDOM;
}();
}
function parseRGBColor(value) {
if (value.indexOf(',') === -1 && isRGBValue(value)) {
return convertRGBToHex(parseInt(value) || 0, 0, 0);
} else {
const splittedValues = value.split(',');
let color = {};
for (let index = 0; index < splittedValues.length; index++) {
const value = splittedValues[index];
if (index === 0) {
color.red = parseInt(value) || 0;
} else if (index === 1) {
color.green = parseInt(value) || 0;
} else if (index === 2) {
color.blue = parseInt(value) || 0;
}
}
if (isRGBValue(color.red) && isRGBValue(color.green) && isRGBValue(color.blue)) {
return convertRGBToHex(color.red, color.green, color.blue);
}
}
}
function isRGBValue(value) {
return value !== undefined && !isNaN(value) && value >= 0 && value <= 255;
}
function convertRGBToHex(red, green, blue) {
return '#' + ((1 << 24) + (red << 16) + (green << 8) + blue).toString(16).slice(1);
}
function parseHexColor(value) {
if (value[0] !== '#') {
value = '#' + value;
}
if (value.length === 4) {
value = (value[0] + value[1] + value[1] + value[2] + value[2] + value[3] + value[3]);
}
if (hexcolor({strict: true}).test(value)) {
return value;
}
}
// exports
module.exports = {
parseRequest,
};

View file

@ -1,139 +1,133 @@
// requirements
const path = require('path');
const config = require('../config');
const packageJSON = require('../package.json');
const logger = require('./logger');
const blinkstick = require('./blinkstick');
// third party requirements
const parser = require('./parser.js');
const express = require('express');
const favicon = require('serve-favicon');
const parser = require('body-parser');
const bodyparser = require('body-parser');
// setup express, blinkstick and other stuff
const app = express();
app.use(favicon(path.join(path.dirname(__dirname), "public", "favicon.ico")));
app.use(parser.json());
app.use(parser.urlencoded({ extended: true }));
app.use(favicon(path.join(path.dirname(__dirname), 'public', 'favicon.ico')));
app.use(bodyparser.json());
app.use(bodyparser.urlencoded({extended: true}));
// get the html content for get requests
// TODO: replace with template engine (vue.js)
function getHTML() {
let welcomeMessage =
"<html>" +
"<head>" +
"<title>" + packageJSON.name + " " + packageJSON.version + "</title>" +
"<style>" +
"body { background: #2a2a2a; color: #f1f1f1; margin: 1.25% }" +
"th, td { border: 1px solid #919191; padding: 6px; text-align: left }" +
"</style>" +
"</head>" +
"<body>" +
"<div>" +
"<h1>" + packageJSON.name + " " + packageJSON.version + "</h1>" +
"</div>" +
"<hr>";
welcomeMessage +=
"<div>" +
"<h2>get:</h2>" +
"<p>" + config.api.get.description + "</p>" +
"</div>";
welcomeMessage +=
"<div>" +
"<h2>post:</h2>" +
"<table style=\"width:100%\">" +
"<th>argument</th>" +
"<th>available values</th>" +
"<th>default</th>" +
"<th>restrictions</th>" +
"<th>description</th>";
Object.keys(config.api.post).forEach(function (argument) {
let restrictions = config.api.post[argument].restrictions || "";
welcomeMessage +=
"<tr>" +
"<td>" + argument + "</td>" +
"<td>" + config.api.post[argument].available + "</td>" +
"<td>" + config.api.post[argument].default + "</td>" +
"<td>" + restrictions + "</td>" +
"<td>" + config.api.post[argument].description + "</td>" +
"</tr>";
});
welcomeMessage +=
"</table>" +
"</div>" +
"</body>" +
"</html>";
return welcomeMessage;
}
const MODE_SET = require('./blinkstick.js').MODE_SET;
const MODE_MORPH = require('./blinkstick.js').MODE_MORPH;
const MODE_BLINK = require('./blinkstick.js').MODE_BLINK;
const MODE_PULSE = require('./blinkstick.js').MODE_PULSE;
const MODE_POWEROFF = require('./blinkstick.js').MODE_POWEROFF;
const html = require('./index.js').get();
// run the express http server and handle gets/posts
function start() {
return new Promise(function (resolve, reject) {
app.listen(config.server.port, config.server.listen)
.on("listening", function () {
return resolve("server listening on " + config.server.listen + ":" + config.server.port + "...")
.on('listening', function () {
return resolve('server listening on ' + config.server.listen + ':' + config.server.port + '...')
})
.on("error", function (err) {
return reject("error starting server (" + err + ")");
.on('error', function (err) {
return reject('error starting server (' + err + ')');
});
});
}
function handleRequests() {
// GET methods
async function handleRequests() {
// GET html page
app.get('*', (request, response) => {
logger.http(request);
response.send(getHTML());
response.send(html);
response.end();
});
app.post('/off', (request, response) => {
// POST '/set'
app.post('/' + MODE_SET, (request, response) => {
logger.http(request);
response.end();
blinkstick.powerOff(parseInt(request.body.index));
blinkstick.simple(parser.parseRequest(request.body, MODE_SET))
.then((result) => {
response.end(JSON.stringify(result));
})
.catch((err) => {
response.status(500);
response.end({status: 'error', error: err});
});
// POST methods
app.post('*', (request, response) => {
});
// POST '/morph'
app.post('/' + MODE_MORPH, (request, response) => {
logger.http(request);
response.end();
blinkstick.illuminate(parseRequest(request.body))
.then(logger.info)
.catch(logger.error);
blinkstick.simple(parser.parseRequest(request.body, MODE_MORPH))
.then((result) => {
response.end(JSON.stringify(result));
})
.catch((err) => {
response.status(500);
response.end({status: 'error', error: err});
});
});
// POST '/blink'
app.post('/' + MODE_BLINK, (request, response) => {
logger.http(request);
const config = parser.parseRequest(request.body, MODE_BLINK);
if (blinkstick.isInfiniteAnimation(config)) {
response.end(JSON.stringify({status: 'ok'}));
response = undefined;
}
blinkstick.complex(config)
.then((result) => {
if (response === undefined) {
return;
}
response.end(JSON.stringify(result));
})
.catch((err) => {
if (response === undefined) {
return;
}
response.status(500);
response.end({status: 'error', error: err});
});
});
// POST '/pulse'
app.post('/' + MODE_PULSE, (request, response) => {
logger.http(request);
const config = parser.parseRequest(request.body, MODE_PULSE);
if (blinkstick.isInfiniteAnimation(config)) {
response.end(JSON.stringify({status: 'ok'}));
response = undefined;
}
blinkstick.complex(config)
.then((result) => {
if (response === undefined) {
return;
}
response.end(JSON.stringify(result));
})
.catch((err) => {
if (response === undefined) {
return;
}
response.status(500);
response.end({status: 'error', error: err});
});
});
// POST '/poweroff'
app.post('/' + MODE_POWEROFF, (request, response) => {
logger.http(request);
blinkstick.powerOff(parser.parseRequest(request.body, MODE_POWEROFF))
.then((result) => {
response.end(JSON.stringify(result));
})
.catch((err) => {
response.status(500);
response.end({status: 'error', error: err});
});
});
}
// parse the request and return an object with sane defaults
function parseRequest(data) {
let blinkstickConfig = {
"id": Math.random(),
"mode": data.mode || config.api.post.mode.default,
"color": blinkstick.parseColor(data.color),
"options": {
"index": blinkstick.LEDS_ALL,
"steps": data.steps,
"duration": data.duration || config.api.post.duration.default,
"pulse": {
"done": 0,
"max": data.pulses || 0
}
}
};
if (data.index !== undefined) {
blinkstickConfig.options.index = parseInt(data.index);
if (blinkstickConfig.options.index < 0) {
blinkstickConfig.options.index = 0;
} else if (blinkstickConfig.options.index > 7) {
blinkstickConfig.options.index = 7;
}
}
if (blinkstickConfig.options.duration < 100) {
blinkstickConfig.options.duration = 100;
}
if (blinkstickConfig.options.index === blinkstick.LEDS_ALL && blinkstickConfig.mode === blinkstick.MODE_MORPH) {
blinkstickConfig.options.duration = blinkstickConfig.options.duration / 8;
}
if (blinkstickConfig.options.steps === undefined || blinkstickConfig.options.steps === 0) {
blinkstickConfig.options.steps = blinkstickConfig.options.duration / 10;
}
return blinkstickConfig;
function checkForInfiniteAnimation(response, config) {
}
// exports