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" "description": "show this page"
}, },
"post": { "post": {
"mode": { "endpoints": [
"available": "set, morph, pulse", "/set",
"default": "set", "/morph",
"description": "specifies the color change mode" "/blink",
}, "/pulse",
"/poweroff"
],
"color": { "color": {
"available": "random, hex color codes (#ffffff), rgb color codes (255, 255, 255)", "available": "random, hex color codes (#ffffff), rgb color codes (255, 255, 255)",
"default": "random", "default": "random",
@ -24,8 +26,8 @@
}, },
"index": { "index": {
"available": "number values", "available": "number values",
"default": "undefined", "default": "all",
"description": "specifies the led index" "description": "specifies the led index / defaults to all leds"
}, },
"duration": { "duration": {
"available": "number values", "available": "number values",
@ -35,13 +37,19 @@
"steps": { "steps": {
"available": "number values", "available": "number values",
"default": "[duration] / 10", "default": "[duration] / 10",
"description": "specifies the amount of steps for the color change" "description": "specifies the number of steps for the color change"
}, },
"pulses": { "repeats": {
"restrictions": "pulse", "restrictions": "blink, pulse",
"available": "number values", "available": "number values",
"default": "0 (infinite)", "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,155 +1,155 @@
// requirements
const logger = require('./logger'); const logger = require('./logger');
const config = require('../config.json');
const util = require('util');
// third party requirements
const blinkstick = require('blinkstick'); const blinkstick = require('blinkstick');
const hexcolor = require('hex-color-regex');
// constants const LED_ANIMATED = 0;
// const LEDS = [0, 1, 2, 3, 4, 5, 6, 7];
const LEDS_ALL = 'index_all'; const LEDS_ALL = 'all';
const RANDOM = 'random';
const ANIMATION_STATE_INPROGRESS = 1;
const ANIMATION_STATE_FINISH = 0;
const MODE_SET = 'set'; const MODE_SET = 'set';
const MODE_MORPH = 'morph'; const MODE_MORPH = 'morph';
const MODE_BLINK = 'blink';
const MODE_PULSE = 'pulse'; const MODE_PULSE = 'pulse';
const MODE_POWEROFF = 'poweroff'; const MODE_POWEROFF = 'poweroff';
// variables
let led;
const LEDS = new Map(); const LEDS = new Map();
let stop = false;
// find a connected blinkstick
function findBlinkstick() { function findBlinkstick() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
led = blinkstick.findFirst(); let led = blinkstick.findFirst();
if (led !== undefined && led !== null) { if (led === undefined) {
return resolve(led); reject('could not find any blinkstick');
} }
return reject('could not find any blinkstick'); resolve(led);
}); });
} }
// light it up // simple animation (set the color / morph to color)
async function illuminate(blinkstickConfig) { async function simple(config) {
led = await findBlinkstick(); try {
let indices = getIndices(blinkstickConfig); await forceStop();
for (let index = 0; index < indices.length; index++) { config.timestamp = new Date().getTime();
try { let indices = getIndices(config);
await setLedState(indices[index], ANIMATION_STATE_INPROGRESS); for (let index = 0; index < indices.length; index++) {
await singleAnimation(JSON.parse(JSON.stringify(blinkstickConfig)), indices[index]); await setLedState(indices[index], LED_ANIMATED);
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}};
let indices = getIndices(config);
for (let index = 0; index < indices.length; index++) {
try {
await singleAnimation(JSON.parse(JSON.stringify(config)), indices[index]); await singleAnimation(JSON.parse(JSON.stringify(config)), indices[index]);
} catch (err) { await clearLedState(indices[index]);
logger.error(err);
} }
return {
status: 'ok',
color: config.color,
indices: indices,
time: (new Date().getTime() - config.timestamp) + 'ms'
};
} catch (err) {
logger.error(err);
return err;
} }
if (index !== LEDS_ALL) { }
logger.info('led \'' + index + '\' powered off');
return; // 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();
}
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'};
}
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;
} }
led.stop();
led.turnOff();
logger.info('blinkstick powered off');
} }
// animations // animations
function singleAnimation(config, index) { async function singleAnimation(config, index) {
return new Promise((resolve, reject) => { try {
config.options.index = index; config.options.index = index;
logger.debug('changing color of led \'' + config.options.index + '\' to \'' + config.color + '\' (mode: ' + config.mode + ')...'); const blinkstick = await findBlinkstick();
switch (config.mode) { return await new Promise((resolve, reject) => {
case MODE_MORPH: logger.debug('changing color of led \'' + config.options.index + '\' to \'' + config.color + '\' (mode: ' + config.mode + ')...');
led.morph(config.color, config.options, callback); switch (config.mode) {
break; case MODE_MORPH:
case MODE_PULSE: blinkstick.morph(config.color, config.options, callback);
led.pulse(config.color, config.options, callback); break;
break; case MODE_BLINK:
default: blinkstick.blink(config.color, config.options, callback);
led.setColor(config.color, config.options, callback); break;
} case MODE_PULSE:
blinkstick.pulse(config.color, config.options, callback);
function callback(err) { break;
clearLedState(config.options.index); default:
if (err) { blinkstick.setColor(config.color, config.options, callback);
reject('changing color of led \'' + config.options.index + '\' to \'' + config.color + '\' encountered an error > ' + err); break;
} }
logger.debug('changed color of led \'' + config.options.index + '\' to \'' + config.color + '\' (mode: ' + config.mode + ')');
resolve(); function callback(err) {
} if (err) {
}); reject('changing color of led \'' + config.options.index + '\' to \'' + config.color + '\' encountered an error > ' + err);
}
logger.debug('changed color of led \'' + config.options.index + '\' to \'' + config.color + '\' (mode: ' + config.mode + ')');
resolve();
}
});
} catch (err) {
return err;
}
} }
// TODO: IMPLEMENT FUNCTION async function forceStop() {
function continousAnimation(config, index) {
}
// start pulsing
function startPulsing(blinkstickConfig) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let animation = getAnimation(blinkstickConfig); stop = true;
if (animation?.state === ANIMATION_STATE_FINISH || (blinkstickConfig.options.pulse.max > 0 && animation.done === blinkstickConfig.options.pulse.max)) { waitForAllAnimationsEnd(() => {
clearAnimationProperties(blinkstickConfig); stop = false;
return resolve('finished pulsing \'' + blinkstickConfig.color + '\''); resolve();
}
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);
}); });
}); });
} }
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 // led / index helper functions
function getIndices(blinkstickConfig) { function getIndices(blinkstickConfig) {
if (blinkstickConfig.options.index === LEDS_ALL) { if (blinkstickConfig.options.index === LEDS_ALL) {
@ -164,7 +164,10 @@ async function setLedState(index, state) {
} }
function clearLedState(index) { function clearLedState(index) {
LEDS.delete(index); return new Promise((resolve, reject) => {
LEDS.delete(index);
resolve();
});
} }
function isLedAnimated(index) { function isLedAnimated(index) {
@ -188,71 +191,35 @@ function waitForAnimationEnd(index, callback) {
}, 100); }, 100);
} }
// color / parser functions function waitForAllAnimationsEnd(callback) {
function parseColor(value) { if (LEDS.size === 0) {
if (!value || value === RANDOM) { return callback();
return RANDOM;
} }
let parsedColor = parseRGBColor(value); setTimeout(() => {
if (!parsedColor) { waitForAllAnimationsEnd(callback);
parsedColor = parseHexColor(value); }, 100);
}
return parsedColor || function () {
logger.warn('could not parse color value \'' + value + '\', defaulting to \'' + config.api.post.color.default + '\'');
return config.api.post.color.default;
}();
} }
function parseRGBColor(value) { function isInfiniteAnimation(config) {
if (value.indexOf(',') === -1 && isRGBValue(value)) { if (config.mode !== MODE_BLINK && config.mode !== MODE_PULSE) {
return convertRGBToHex(parseInt(value) || 0, 0, 0); return false;
} 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);
}
} }
} if (config.options.index === LEDS_ALL) {
return config.repetitions.max === 0;
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 // exports
module.exports = { module.exports = {
parseColor, simple,
illuminate, complex,
powerOff, powerOff,
isInfiniteAnimation,
LEDS_ALL, LEDS_ALL,
MODE_SET, MODE_SET,
MODE_MORPH, MODE_MORPH,
MODE_BLINK,
MODE_PULSE, MODE_PULSE,
MODE_POWEROFF 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) { if (global.config !== undefined && global.config.log !== undefined && global.config.log.format !== undefined) {
return global.config.log.timestamp; 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 // 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 path = require('path');
const config = require('../config'); const config = require('../config');
const packageJSON = require('../package.json'); const packageJSON = require('../package.json');
const logger = require('./logger'); const logger = require('./logger');
const blinkstick = require('./blinkstick'); const blinkstick = require('./blinkstick');
// third party requirements const parser = require('./parser.js');
const express = require('express'); const express = require('express');
const favicon = require('serve-favicon'); const favicon = require('serve-favicon');
const parser = require('body-parser'); const bodyparser = require('body-parser');
// setup express, blinkstick and other stuff
const app = express(); const app = express();
app.use(favicon(path.join(path.dirname(__dirname), "public", "favicon.ico"))); app.use(favicon(path.join(path.dirname(__dirname), 'public', 'favicon.ico')));
app.use(parser.json()); app.use(bodyparser.json());
app.use(parser.urlencoded({ extended: true })); app.use(bodyparser.urlencoded({extended: true}));
// get the html content for get requests const MODE_SET = require('./blinkstick.js').MODE_SET;
// TODO: replace with template engine (vue.js) const MODE_MORPH = require('./blinkstick.js').MODE_MORPH;
function getHTML() { const MODE_BLINK = require('./blinkstick.js').MODE_BLINK;
let welcomeMessage = const MODE_PULSE = require('./blinkstick.js').MODE_PULSE;
"<html>" + const MODE_POWEROFF = require('./blinkstick.js').MODE_POWEROFF;
"<head>" +
"<title>" + packageJSON.name + " " + packageJSON.version + "</title>" + const html = require('./index.js').get();
"<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;
}
// run the express http server and handle gets/posts // run the express http server and handle gets/posts
function start() { function start() {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
app.listen(config.server.port, config.server.listen) app.listen(config.server.port, config.server.listen)
.on("listening", function () { .on('listening', function () {
return resolve("server listening on " + config.server.listen + ":" + config.server.port + "...") return resolve('server listening on ' + config.server.listen + ':' + config.server.port + '...')
}) })
.on("error", function (err) { .on('error', function (err) {
return reject("error starting server (" + err + ")"); return reject('error starting server (' + err + ')');
}); });
}); });
} }
function handleRequests() { async function handleRequests() {
// GET methods // GET html page
app.get('*', (request, response) => { app.get('*', (request, response) => {
logger.http(request); logger.http(request);
response.send(getHTML()); response.send(html);
response.end(); response.end();
}); });
app.post('/off', (request, response) => { // POST '/set'
app.post('/' + MODE_SET, (request, response) => {
logger.http(request); logger.http(request);
response.end(); blinkstick.simple(parser.parseRequest(request.body, MODE_SET))
blinkstick.powerOff(parseInt(request.body.index)); .then((result) => {
response.end(JSON.stringify(result));
})
.catch((err) => {
response.status(500);
response.end({status: 'error', error: err});
});
}); });
// POST methods // POST '/morph'
app.post('*', (request, response) => { app.post('/' + MODE_MORPH, (request, response) => {
logger.http(request); logger.http(request);
response.end(); blinkstick.simple(parser.parseRequest(request.body, MODE_MORPH))
blinkstick.illuminate(parseRequest(request.body)) .then((result) => {
.then(logger.info) response.end(JSON.stringify(result));
.catch(logger.error); })
.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 checkForInfiniteAnimation(response, config) {
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;
} }
// exports // exports