optimized and extended functionality
This commit is contained in:
parent
069faa91f8
commit
faee049169
7 changed files with 194 additions and 88 deletions
|
@ -4,7 +4,7 @@ control your blinkstick via http requests
|
|||
|
||||
## requirements
|
||||
|
||||
- nodejs
|
||||
- nodejs 8.x
|
||||
- yarn / npm
|
||||
- node-gyp (probably; to build the node-hid module)
|
||||
|
||||
|
|
38
blinky.js
38
blinky.js
|
@ -1,23 +1,41 @@
|
|||
// requirements
|
||||
const blinkstick = require('./libs/blinkstick');
|
||||
const server = require('./libs/server');
|
||||
const logger = require('./libs/logger');
|
||||
const server = require('./libs/server.js');
|
||||
const util = require('./libs/util.js');
|
||||
const logger = require('./libs/logger.js');
|
||||
const packageJSON = require('./package');
|
||||
|
||||
const INTERRUPTS = ['SIGINT', 'SIGTERM'];
|
||||
|
||||
global.config = process.argv[2] || __dirname + '/config.json';
|
||||
|
||||
// handle interrupts
|
||||
handleInterrupts();
|
||||
|
||||
// start the application
|
||||
main();
|
||||
|
||||
// main - let's get this party started
|
||||
function main() {
|
||||
server.start()
|
||||
util.fileExists(global.config)
|
||||
.then((result) => {
|
||||
global.config = require(result.path);
|
||||
global.config.path = result.path;
|
||||
})
|
||||
.then(logger.initialize)
|
||||
.then(server.start)
|
||||
.then(logger.info)
|
||||
.then(server.handleRequests)
|
||||
.catch(exit);
|
||||
}
|
||||
|
||||
// ... and it all comes crashing down
|
||||
function exit(err) {
|
||||
let code = 0;
|
||||
function exit(err, code) {
|
||||
if (code == undefined) {
|
||||
code = 0;
|
||||
if (err != undefined) {
|
||||
code = 1;
|
||||
}
|
||||
}
|
||||
if (err) {
|
||||
logger.error(err);
|
||||
logger.error(packageJSON.name + " ended due to an error");
|
||||
|
@ -26,3 +44,11 @@ function exit(err) {
|
|||
}
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
function handleInterrupts() {
|
||||
for (var index = 0; index < INTERRUPTS.length; index++) {
|
||||
process.once(INTERRUPTS[index], (code) => {
|
||||
exit(null, code);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"server": {
|
||||
"listen": "0.0.0.0",
|
||||
"port": 3000,
|
||||
"timestamp": "DD.MM.YYYY HH:mm:ss:SS"
|
||||
"port": 3000
|
||||
},
|
||||
"log": {
|
||||
"level": "info"
|
||||
"level": "debug",
|
||||
"timestamp": "DD.MM.YYYY HH:mm:ss:SS"
|
||||
},
|
||||
"api": {
|
||||
"get": {
|
||||
|
|
|
@ -7,8 +7,7 @@ const blinkstick = require('blinkstick');
|
|||
const hexcolor = require('hex-color-regex');
|
||||
|
||||
// constants
|
||||
const RANDOM = "random"
|
||||
const ANIMATION_STATE_INFINITE = 2;
|
||||
const RANDOM = 'random'
|
||||
const ANIMATION_STATE_INPROGRESS = 1;
|
||||
const ANIMATION_STATE_FINISH = 0;
|
||||
|
||||
|
@ -17,19 +16,27 @@ let led;
|
|||
let animation = {};
|
||||
|
||||
function findBlinkstick() {
|
||||
return new Promise(function (resolve, reject) {
|
||||
return new Promise((resolve, reject) => {
|
||||
led = blinkstick.findFirst();
|
||||
if (led !== undefined && led !== null) {
|
||||
return resolve(led);
|
||||
}
|
||||
return reject("could not find any blinkstick");
|
||||
return reject('could not find any blinkstick');
|
||||
});
|
||||
}
|
||||
|
||||
// turn the blinkstick off
|
||||
function powerOff() {
|
||||
return new Promise((resolve, reject) => {
|
||||
findBlinkstick()
|
||||
.then((led) => {
|
||||
led.stop();
|
||||
led.turnOff();
|
||||
logger.info("blinkstick powered off");
|
||||
logger.info('blinkstick powered off');
|
||||
resolve();
|
||||
})
|
||||
.catch(reject);
|
||||
})
|
||||
}
|
||||
|
||||
// parse a color object from given request arguments
|
||||
|
@ -42,17 +49,17 @@ function parseColor(value) {
|
|||
parsedColor = parseHexColor(value);
|
||||
}
|
||||
return parsedColor || function () {
|
||||
logger.warn("could not parse color value '" + value + "', defaulting to '" + config.api.post.color.default + "'");
|
||||
logger.warn('could not parse color value \'' + value + '\', defaulting to \'' + config.api.post.color.default + '\'');
|
||||
return config.api.post.color.default;
|
||||
}();
|
||||
}
|
||||
|
||||
// parse rgb color values
|
||||
function parseRGBColor(value) {
|
||||
if (value.indexOf(",") == -1 && isRGBValue(value)) {
|
||||
if (value.indexOf(',') == -1 && isRGBValue(value)) {
|
||||
return convertRGBToHex(parseInt(value) || 0, 0, 0);
|
||||
} else {
|
||||
const splittedValues = value.split(",");
|
||||
const splittedValues = value.split(',');
|
||||
let color = {};
|
||||
for (let index = 0; index < splittedValues.length; index++) {
|
||||
const value = splittedValues[index];
|
||||
|
@ -77,7 +84,7 @@ function isRGBValue(value) {
|
|||
|
||||
// convert rgb to hex color values
|
||||
function convertRGBToHex(red, green, blue) {
|
||||
return "#" + ((1 << 24) + (red << 16) + (green << 8) + blue).toString(16).slice(1);
|
||||
return '#' + ((1 << 24) + (red << 16) + (green << 8) + blue).toString(16).slice(1);
|
||||
}
|
||||
|
||||
// parse hex color values
|
||||
|
@ -99,10 +106,10 @@ function illuminate(blinkstickConfig) {
|
|||
findBlinkstick()
|
||||
.then(function () {
|
||||
switch (blinkstickConfig.mode) {
|
||||
case "morph":
|
||||
case 'morph':
|
||||
morph(blinkstickConfig, resolve, reject);
|
||||
break;
|
||||
case "pulse":
|
||||
case 'pulse':
|
||||
startPulsing(blinkstickConfig, resolve, reject);
|
||||
break;
|
||||
default:
|
||||
|
@ -119,13 +126,13 @@ function setColor(blinkstickConfig, resolve, reject) {
|
|||
stopAnimation()
|
||||
.then(function () {
|
||||
setAnimationProperties(blinkstickConfig);
|
||||
logger.debug("setting color to '" + blinkstickConfig.color + "'...");
|
||||
logger.debug('setting color to \'' + blinkstickConfig.color + '\'...');
|
||||
led.setColor(blinkstickConfig.color, blinkstickConfig.options, function (err) {
|
||||
clearAnimationProperties();
|
||||
if (err) {
|
||||
return reject("error setting color '" + blinkstickConfig.color + "' (" + err + ")");
|
||||
return reject('error setting color \'' + blinkstickConfig.color + '\' (' + err + ')');
|
||||
}
|
||||
return resolve("set color to '" + blinkstickConfig.color + "'");
|
||||
return resolve('set color to \'' + blinkstickConfig.color + '\'');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -135,22 +142,23 @@ function morph(blinkstickConfig, resolve, reject) {
|
|||
stopAnimation()
|
||||
.then(function () {
|
||||
setAnimationProperties(blinkstickConfig);
|
||||
logger.debug("morphing color to '" + blinkstickConfig.color + "'...");
|
||||
logger.debug('morphing color to \'' + blinkstickConfig.color + '\'...');
|
||||
logger.debug('OPTS: ' + JSON.stringify(blinkstickConfig.options));
|
||||
led.morph(blinkstickConfig.color, blinkstickConfig.options, function (err) {
|
||||
clearAnimationProperties();
|
||||
if (err) {
|
||||
return reject("error morphing color to '" + blinkstickConfig.color + "' (" + err + ")");
|
||||
return reject('error morphing color to \'' + blinkstickConfig.color + '\' (' + err + ')');
|
||||
}
|
||||
return resolve("morphed color to '" + blinkstickConfig.color + "'");
|
||||
return resolve('morphed color to \'' + blinkstickConfig.color + '\'');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// start pulsing
|
||||
function startPulsing(blinkstickConfig, 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))) {
|
||||
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 '" + blinkstickConfig.color + "'");
|
||||
return resolve('finished pulsing color \'' + blinkstickConfig.color + '\'');
|
||||
}
|
||||
if (animation.id && animation.id != blinkstickConfig.id) {
|
||||
stopAnimation()
|
||||
|
@ -165,10 +173,10 @@ function startPulsing(blinkstickConfig, resolve, reject) {
|
|||
led.pulse(blinkstickConfig.color, blinkstickConfig.options, function (err) {
|
||||
if (err) {
|
||||
clearAnimationProperties();
|
||||
return reject("error pulsing color '" + blinkstickConfig.color + "' (" + err + ")");
|
||||
return reject('error pulsing color \'' + blinkstickConfig.color + '\' (' + err + ')');
|
||||
}
|
||||
blinkstickConfig.options.pulse.done++;
|
||||
logger.debug("pulsed color '" + blinkstickConfig.color + "' " + blinkstickConfig.options.pulse.done + "/" + blinkstickConfig.options.pulse.max + " times");
|
||||
logger.debug('pulsed color \'' + blinkstickConfig.color + '\' ' + blinkstickConfig.options.pulse.done + '/' + blinkstickConfig.options.pulse.max + ' times');
|
||||
startPulsing(blinkstickConfig, resolve, reject);
|
||||
});
|
||||
}
|
||||
|
@ -180,12 +188,8 @@ function setAnimationProperties(blinkstickConfig) {
|
|||
}
|
||||
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() {
|
||||
|
@ -219,11 +223,6 @@ 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,
|
||||
|
|
|
@ -1,21 +1,35 @@
|
|||
// requirements
|
||||
const config = require("../config.json");
|
||||
// third party requirements
|
||||
const moment = require("moment");
|
||||
const moment = require('moment');
|
||||
|
||||
// constants
|
||||
const LOG_PREFIX_DEBUG = "debug";
|
||||
const LOG_PREFIX_INFO = "info";
|
||||
const LOG_PREFIX_WARNING = "warning";
|
||||
const LOG_PREFIX_ERROR = "error";
|
||||
const LOG_PREFIX_DEBUG = 'debug';
|
||||
const LOG_PREFIX_INFO = 'info';
|
||||
const LOG_PREFIX_WARNING = 'warning';
|
||||
const LOG_PREFIX_ERROR = 'error';
|
||||
const LOGLEVEL_DEBUG = 0;
|
||||
const LOGLEVEL_INFO = 1;
|
||||
const LOGLEVEL_WARNING = 2;
|
||||
const LOGLEVEL_ERROR = 3;
|
||||
|
||||
// set loglevel on "require"
|
||||
const loglevel = function() {
|
||||
switch (config.log.level) {
|
||||
var loglevel = getLogLevel();
|
||||
var timestamp = getTimestamp();
|
||||
|
||||
function initialize() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (global.config == undefined) {
|
||||
reject('could not initialize logger, config is undefined');
|
||||
}
|
||||
loglevel = getLogLevel();
|
||||
timestamp = getTimestamp();
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
// get the loglevel
|
||||
function getLogLevel() {
|
||||
if (global.config == undefined || global.config.log == undefined || global.config.log.level == undefined) {
|
||||
return LOGLEVEL_INFO;
|
||||
}
|
||||
switch (global.config.log.level) {
|
||||
case LOG_PREFIX_DEBUG:
|
||||
case LOGLEVEL_DEBUG:
|
||||
return LOGLEVEL_DEBUG;
|
||||
|
@ -31,20 +45,31 @@ const loglevel = function() {
|
|||
default:
|
||||
return LOGLEVEL_INFO;
|
||||
}
|
||||
}();
|
||||
}
|
||||
|
||||
// get the timestamp format
|
||||
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";
|
||||
}
|
||||
|
||||
// log a http request
|
||||
function logRequest(request) {
|
||||
let message = "[" + request.method + "] url: \"" + request.url + "\"";
|
||||
function http(request) {
|
||||
if (request == undefined) {
|
||||
return;
|
||||
}
|
||||
let message = '[' + request.method + '] url: \'' + request.url + '\'';
|
||||
let counter = 1;
|
||||
for (let param in request.body) {
|
||||
message += ", parameter " + counter + ": \"" + param + "=" + request.body[param] + "\"";
|
||||
message += ', parameter ' + counter + ': \'' + param + '=' + request.body[param] + '\'';
|
||||
counter++;
|
||||
}
|
||||
debug(message.trim());
|
||||
}
|
||||
|
||||
// prefix log with "info"
|
||||
// prefix log with 'info'
|
||||
function info(message) {
|
||||
if (loglevel > LOGLEVEL_INFO) {
|
||||
return;
|
||||
|
@ -52,28 +77,34 @@ function info(message) {
|
|||
trace(message);
|
||||
}
|
||||
|
||||
// prefix log with "info"
|
||||
// prefix log with 'info'
|
||||
function warn(message) {
|
||||
if (loglevel > LOGLEVEL_WARNING) {
|
||||
return;
|
||||
}
|
||||
trace(message, "warning");
|
||||
trace(message, 'warning');
|
||||
}
|
||||
|
||||
// prefix log with "debug"
|
||||
// prefix log with 'debug'
|
||||
function debug(message) {
|
||||
if (loglevel > LOGLEVEL_DEBUG) {
|
||||
return;
|
||||
}
|
||||
trace(message, "debug");
|
||||
trace(message, 'debug');
|
||||
}
|
||||
|
||||
// prefix log with "error"
|
||||
// prefix log with 'error'
|
||||
function error(message) {
|
||||
if (loglevel > LOGLEVEL_ERROR) {
|
||||
return;
|
||||
}
|
||||
trace(message, "error");
|
||||
if (message.errors != undefined) {
|
||||
for (var index = 0; index < message.errors.length; index++) {
|
||||
trace(message.errors[index], 'error');
|
||||
}
|
||||
return;
|
||||
}
|
||||
trace(message, 'error');
|
||||
}
|
||||
|
||||
// default logging function
|
||||
|
@ -82,31 +113,32 @@ function trace(message, prefix) {
|
|||
return;
|
||||
}
|
||||
if (prefix === undefined || prefix === null || prefix.length === 0) {
|
||||
prefix = "info";
|
||||
prefix = 'info';
|
||||
}
|
||||
let print;
|
||||
switch (prefix) {
|
||||
case "error":
|
||||
case 'error':
|
||||
print = console.error;
|
||||
break;
|
||||
case "debug":
|
||||
case 'debug':
|
||||
print = console.debug;
|
||||
break;
|
||||
case "warning":
|
||||
case 'warning':
|
||||
print = console.warn;
|
||||
break;
|
||||
default:
|
||||
print = console.log;
|
||||
}
|
||||
message = moment().format(config.server.timestamp) + " | " + prefix + " > " + message;
|
||||
message = moment().format(timestamp) + ' | ' + prefix + ' > ' + message;
|
||||
print(message);
|
||||
}
|
||||
|
||||
// exports
|
||||
module.exports = {
|
||||
initialize,
|
||||
info,
|
||||
warn,
|
||||
debug,
|
||||
error,
|
||||
logRequest
|
||||
};
|
||||
http
|
||||
}
|
|
@ -77,19 +77,23 @@ function start() {
|
|||
|
||||
function handleRequests() {
|
||||
// GET methods
|
||||
app.get('*', function (request, response) {
|
||||
logger.logRequest(request);
|
||||
app.get('*', (request, response) => {
|
||||
logger.http(request);
|
||||
response.send(getHTML());
|
||||
response.end();
|
||||
});
|
||||
app.post('/off', (request, response) => {
|
||||
logger.http(request);
|
||||
response.end();
|
||||
blinkstick.powerOff();
|
||||
});
|
||||
// POST methods
|
||||
app.post('*', function (request, response) {
|
||||
logger.logRequest(request);
|
||||
app.post('*', (request, response) => {
|
||||
logger.http(request);
|
||||
response.end();
|
||||
blinkstick.illuminate(parseRequest(request.body))
|
||||
.then(logger.info)
|
||||
.catch(logger.error);
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -97,20 +101,26 @@ function handleRequests() {
|
|||
function parseRequest(data) {
|
||||
let blinkstickConfig = {
|
||||
"id": Math.random(),
|
||||
"mode": data["mode"] || config.api.post.mode.default,
|
||||
"color": blinkstick.parseColor(data["color"]),
|
||||
"mode": data.mode || config.api.post.mode.default,
|
||||
"color": blinkstick.parseColor(data.color),
|
||||
"options": {
|
||||
"duration": data["duration"] || config.api.post.duration.default,
|
||||
"steps": data.steps,
|
||||
"duration": data.duration || config.api.post.duration.default,
|
||||
"pulse": {
|
||||
"max": data["pulses"] || 0,
|
||||
"max": data.pulses || 0,
|
||||
"done": 0
|
||||
}
|
||||
}
|
||||
};
|
||||
blinkstickConfig.options.steps = blinkstickConfig.options.duration / 10;
|
||||
if (data.index != undefined) {
|
||||
blinkstickConfig.options.index = data.index;
|
||||
}
|
||||
if (blinkstickConfig.options.duration < 100) {
|
||||
blinkstickConfig.options.duration = 100;
|
||||
}
|
||||
if (blinkstickConfig.options.steps == undefined || blinkstickConfig.options.steps == 0) {
|
||||
blinkstickConfig.options.steps = blinkstickConfig.options.duration / 10;
|
||||
}
|
||||
return blinkstickConfig;
|
||||
}
|
||||
|
||||
|
|
39
libs/util.js
Normal file
39
libs/util.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
const realpath = require('fs').realpath;
|
||||
const stat = require('fs').stat;
|
||||
|
||||
function fileExists(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (file == undefined) {
|
||||
reject('can not check the existence of an undefined file');
|
||||
}
|
||||
resolvePath(file)
|
||||
.then((path) => {
|
||||
stat(path, (err, stats) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
resolve({ path, stats });
|
||||
})
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
function resolvePath(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (file == undefined) {
|
||||
reject('can not resolve a path to an undefined file');
|
||||
}
|
||||
realpath(file, (err, resolvedPath) => {
|
||||
if (err) {
|
||||
reject('resolving path \'' + file + '\' encountered an error >>> ' + err);
|
||||
}
|
||||
resolve(resolvedPath);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
fileExists,
|
||||
resolvePath
|
||||
}
|
Loading…
Reference in a new issue