From 61c43fdd9b849ac258f7196bf5518bfdd2e1d147 Mon Sep 17 00:00:00 2001 From: velvettear Date: Wed, 23 Feb 2022 17:08:41 +0100 Subject: [PATCH] reworked almost the whole project - everything should work now as expected --- config.json | 30 +++-- libs/blinkstick.js | 311 ++++++++++++++++++++------------------------- libs/index.js | 66 ++++++++++ libs/logger.js | 2 +- libs/parser.js | 188 +++++++++++++++++++++++++++ libs/server.js | 206 +++++++++++++++--------------- 6 files changed, 513 insertions(+), 290 deletions(-) create mode 100644 libs/index.js create mode 100644 libs/parser.js diff --git a/config.json b/config.json index 135fd37..8c1758b 100644 --- a/config.json +++ b/config.json @@ -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 " } } } diff --git a/libs/blinkstick.js b/libs/blinkstick.js index 9bd778d..441b866 100644 --- a/libs/blinkstick.js +++ b/libs/blinkstick.js @@ -1,155 +1,155 @@ -// 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++) { - 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}}; - let indices = getIndices(config); - for (let index = 0; index < indices.length; index++) { - try { +// simple animation (set the color / morph to color) +async function simple(config) { + try { + await forceStop(); + config.timestamp = new Date().getTime(); + let indices = getIndices(config); + for (let index = 0; index < indices.length; index++) { + await setLedState(indices[index], LED_ANIMATED); await singleAnimation(JSON.parse(JSON.stringify(config)), indices[index]); - } catch (err) { - logger.error(err); + 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; } - 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 -function singleAnimation(config, index) { - return new Promise((resolve, reject) => { +async function singleAnimation(config, index) { + try { config.options.index = index; - 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); - break; - case MODE_PULSE: - led.pulse(config.color, config.options, callback); - break; - default: - led.setColor(config.color, config.options, callback); - } - - function callback(err) { - clearLedState(config.options.index); - if (err) { - reject('changing color of led \'' + config.options.index + '\' to \'' + config.color + '\' encountered an error > ' + err); + 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: + blinkstick.morph(config.color, config.options, callback); + break; + case MODE_BLINK: + blinkstick.blink(config.color, config.options, callback); + break; + case MODE_PULSE: + blinkstick.pulse(config.color, config.options, callback); + break; + default: + blinkstick.setColor(config.color, config.options, callback); + 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 -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) { - LEDS.delete(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; - } - } - if (isRGBValue(color.red) && isRGBValue(color.green) && isRGBValue(color.blue)) { - return convertRGBToHex(color.red, color.green, color.blue); - } +function isInfiniteAnimation(config) { + if (config.mode !== MODE_BLINK && config.mode !== MODE_PULSE) { + return false; } -} - -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; + if (config.options.index === LEDS_ALL) { + return config.repetitions.max === 0; } + 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 }; \ No newline at end of file diff --git a/libs/index.js b/libs/index.js new file mode 100644 index 0000000..c75366a --- /dev/null +++ b/libs/index.js @@ -0,0 +1,66 @@ +const packageJSON = require('../package.json'); +const config = require('../config.json'); + +function get() { + let html = + '' + + '' + + '' + packageJSON.name + ' ' + packageJSON.version + '' + + '' + + '' + + '' + + '
' + + '

' + packageJSON.name + ' ' + packageJSON.version + '

' + + '
' + + '
'; + html += + '
' + + '

get:

' + + '

' + config.api.get.description + '

' + + '
'; + + html += + '
' + + '

post:

' + + '

endpoints: '; + for (let index = 0; index < config.api.post.endpoints.length; index++) { + if (index > 0) { + html += ', '; + } + html += config.api.post.endpoints[index]; + } + html += '

' + + '' + + '' + + '' + + '' + + '' + + ''; + Object.keys(config.api.post).forEach((argument) => { + if (argument === 'endpoints') { + return; + } + let restrictions = config.api.post[argument].restrictions || ''; + html += + '' + + '' + + '' + + '' + + '' + + '' + + ''; + }); + html += + '
argumentavailable valuesdefaultrestrictionsdescription
' + argument + '' + config.api.post[argument].available + '' + config.api.post[argument].default + '' + restrictions + '' + config.api.post[argument].description + '
' + + '
' + + '' + + ''; + return html; +} + +module.exports = { + get +}; \ No newline at end of file diff --git a/libs/logger.js b/libs/logger.js index 86ca691..4f33666 100644 --- a/libs/logger.js +++ b/libs/logger.js @@ -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 diff --git a/libs/parser.js b/libs/parser.js new file mode 100644 index 0000000..188f382 --- /dev/null +++ b/libs/parser.js @@ -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, +}; \ No newline at end of file diff --git a/libs/server.js b/libs/server.js index 86a7d21..5b230d2 100644 --- a/libs/server.js +++ b/libs/server.js @@ -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 = - "" + - "" + - "" + packageJSON.name + " " + packageJSON.version + "" + - "" + - "" + - "" + - "
" + - "

" + packageJSON.name + " " + packageJSON.version + "

" + - "
" + - "
"; - welcomeMessage += - "
" + - "

get:

" + - "

" + config.api.get.description + "

" + - "
"; - welcomeMessage += - "
" + - "

post:

" + - "" + - "" + - "" + - "" + - "" + - ""; - Object.keys(config.api.post).forEach(function (argument) { - let restrictions = config.api.post[argument].restrictions || ""; - welcomeMessage += - "" + - "" + - "" + - "" + - "" + - "" + - ""; - }); - welcomeMessage += - "
argumentavailable valuesdefaultrestrictionsdescription
" + argument + "" + config.api.post[argument].available + "" + config.api.post[argument].default + "" + restrictions + "" + config.api.post[argument].description + "
" + - "
" + - "" + - ""; - 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