2022-02-28 01:53:13 +01:00
const logger = require ( './logger.js' ) ;
const util = require ( './util.js' ) ;
2022-03-01 22:14:36 +01:00
const constants = require ( './constants.js' ) ;
2021-04-06 16:41:49 +02:00
const blinkstick = require ( 'blinkstick' ) ;
2022-02-28 01:53:13 +01:00
const LEDAnimations = new Map ( ) ;
2021-04-06 16:41:49 +02:00
2022-02-25 15:40:20 +01:00
let blinksticks ;
2022-02-24 10:57:41 +01:00
// find connected blinkstick(s)
2022-02-25 15:40:20 +01:00
async function findBlinkstick ( index , ignoreFilter ) {
if ( ! global . config . blinkstick ? . cache || blinksticks === undefined ) {
blinksticks = blinkstick . findAll ( ) ;
2022-03-03 21:18:07 +01:00
if ( ! ignoreFilter && global . config . blinkstick ? . serials ? . length > 0 ) {
2022-02-25 15:50:32 +01:00
blinksticks = blinksticks . filter ( ( blinkstick ) => {
return global . config . blinkstick . serials . includes ( blinkstick . serial ) ;
} ) ;
if ( blinksticks . length === 0 ) {
throw new Error ( 'could not find any blinkstick matching the defined serial(s)' ) ;
}
}
2022-02-25 15:40:20 +01:00
}
2022-02-24 10:57:41 +01:00
if ( blinksticks . length === 0 ) {
2022-02-25 15:40:20 +01:00
throw new Error ( 'could not find any blinkstick, make sure at least one blinkstick is connected' ) ;
2022-02-24 10:57:41 +01:00
}
if ( index === undefined ) {
index = 0 ;
2022-03-01 22:14:36 +01:00
} else if ( index !== constants . ALL ) {
2022-03-03 21:18:07 +01:00
index = parseInt ( index ) || 0 ;
2022-02-25 15:40:20 +01:00
}
if ( index > blinksticks . length - 1 ) {
throw new Error ( 'there is no blinkstick for index \'' + index + '\'' ) ;
2022-02-24 10:57:41 +01:00
}
2022-03-01 22:14:36 +01:00
if ( index === constants . ALL ) {
2022-02-25 15:40:20 +01:00
return blinksticks ;
2022-02-24 10:57:41 +01:00
}
2022-02-25 15:40:20 +01:00
return blinksticks [ index ] ;
2021-04-06 16:41:49 +02:00
}
2022-02-23 17:08:41 +01:00
// simple animation (set the color / morph to color)
async function simple ( config ) {
2022-02-28 01:53:13 +01:00
await stopLEDsAccordingly ( config ) ;
2022-02-24 11:52:16 +01:00
config . timestamp = new Date ( ) . getTime ( ) ;
2022-03-02 00:18:14 +01:00
let indexes = getIndices ( config ) ;
for ( let index = 0 ; index < indexes . length ; index ++ ) {
2022-02-28 01:53:13 +01:00
const tmpConfig = JSON . parse ( JSON . stringify ( config ) ) ;
2022-03-02 00:18:14 +01:00
await singleAnimation ( tmpConfig , indexes [ index ] ) ;
2022-02-28 02:14:12 +01:00
if ( index === 0 ) {
2022-03-01 22:14:36 +01:00
await setColorIfRandom ( config ) ;
2022-02-28 02:14:12 +01:00
}
2022-02-22 17:00:30 +01:00
}
2022-02-24 11:52:16 +01:00
return {
status : 'ok' ,
color : config . color ,
2022-03-02 00:18:14 +01:00
indexes : indexes ,
2022-02-24 11:52:16 +01:00
time : ( new Date ( ) . getTime ( ) - config . timestamp ) + 'ms'
} ;
2022-02-22 17:00:30 +01:00
}
2022-02-23 17:08:41 +01:00
// complex animation (pulse / blink)
async function complex ( config ) {
2022-02-24 11:52:16 +01:00
if ( config . timestamp === undefined ) {
2022-02-28 01:53:13 +01:00
await stopLEDsAccordingly ( config ) ;
2022-02-24 11:52:16 +01:00
config . timestamp = new Date ( ) . getTime ( ) ;
}
2022-03-02 00:18:14 +01:00
let indexes = getIndices ( config ) ;
for ( let index = 0 ; index < indexes . length ; index ++ ) {
2022-02-28 01:53:13 +01:00
if ( shouldLEDFinish ( config ) ) {
clearLedState ( config . options . index ) ;
2022-02-25 22:59:01 +01:00
return { status : 'ok' , time : ( new Date ( ) . getTime ( ) - config . timestamp ) + 'ms' } ;
2022-02-23 17:08:41 +01:00
}
2022-02-28 01:53:13 +01:00
const tmpConfig = JSON . parse ( JSON . stringify ( config ) ) ;
2022-03-02 00:18:14 +01:00
await singleAnimation ( tmpConfig , indexes [ index ] ) ;
2022-02-24 14:53:17 +01:00
config . repetitions . done ++ ;
2022-02-22 17:00:30 +01:00
}
2022-02-28 01:53:13 +01:00
return await complex ( config ) ;
2022-02-22 16:42:49 +01:00
}
2022-02-23 17:08:41 +01:00
// power the blinkstick (or just a specific led) off
async function powerOff ( config ) {
2022-02-24 11:52:16 +01:00
config . timestamp = new Date ( ) . getTime ( ) ;
2022-03-02 00:18:14 +01:00
let indexes = getIndices ( config ) ;
2022-03-01 22:14:36 +01:00
if ( config . options . index === constants . ALL ) {
LEDAnimations . set ( constants . ALL , { stop : new Date ( ) . getTime ( ) } ) ;
2022-02-28 01:53:13 +01:00
}
2022-03-02 00:18:14 +01:00
for ( let index = 0 ; index < indexes . length ; index ++ ) {
await stopLEDAnimation ( indexes [ index ] ) ;
await singleAnimation ( JSON . parse ( JSON . stringify ( config ) ) , indexes [ index ] ) ;
logger . info ( 'led \'' + indexes [ index ] + '\' powered off' ) ;
2022-02-22 16:42:49 +01:00
}
2022-03-01 22:14:36 +01:00
if ( config . options . index === constants . ALL ) {
2022-02-24 11:52:16 +01:00
const blinkstick = await findBlinkstick ( ) ;
blinkstick . turnOff ( ) ;
2022-02-28 01:53:13 +01:00
LEDAnimations . clear ( ) ;
2022-02-24 11:52:16 +01:00
logger . info ( 'blinkstick powered off' ) ;
}
2022-03-02 00:18:14 +01:00
return { status : 'ok' , indexes : indexes , time : ( new Date ( ) . getTime ( ) - config . timestamp ) + 'ms' } ;
2022-02-22 16:42:49 +01:00
}
// animations
2022-02-23 17:08:41 +01:00
async function singleAnimation ( config , index ) {
2022-02-24 11:52:16 +01:00
config . options . index = index ;
const blinkstick = await findBlinkstick ( ) ;
return await new Promise ( ( resolve , reject ) => {
2022-02-28 01:53:13 +01:00
logger . debug ( 'changing color of led \'' + config . options . index + '\' to \'' + config . color + '\' (mode: ' + config . mode + ' | options: ' + JSON . stringify ( config . options ) + ')...' ) ;
setLEDAnimated ( config . options . index ) ;
blinkstick . animationsEnabled = true ;
2022-02-24 11:52:16 +01:00
switch ( config . mode ) {
2022-03-01 22:14:36 +01:00
case constants . MODE _MORPH :
2022-02-24 11:52:16 +01:00
blinkstick . morph ( config . color , config . options , callback ) ;
break ;
2022-03-01 22:14:36 +01:00
case constants . MODE _BLINK :
2022-02-24 11:52:16 +01:00
blinkstick . blink ( config . color , config . options , callback ) ;
break ;
2022-03-01 22:14:36 +01:00
case constants . MODE _PULSE :
2022-02-24 11:52:16 +01:00
blinkstick . pulse ( config . color , config . options , callback ) ;
break ;
default :
blinkstick . setColor ( config . color , config . options , callback ) ;
break ;
}
2022-02-22 17:00:30 +01:00
2022-02-24 11:52:16 +01:00
function callback ( err ) {
2022-03-01 22:14:36 +01:00
if ( config . mode !== constants . MODE _BLINK && config . mode !== constants . MODE _PULSE ) {
2022-02-28 01:53:13 +01:00
clearLedState ( config . options . index ) ;
}
2022-02-24 11:52:16 +01:00
if ( err ) {
reject ( new Error ( 'changing color of led \'' + config . options . index + '\' to \'' + config . color + '\' encountered an error > ' + err ) ) ;
2022-02-23 17:08:41 +01:00
}
2022-02-25 22:59:01 +01:00
logger . info ( 'changed color of led \'' + config . options . index + '\' to \'' + config . color + '\' (mode: ' + config . mode + ')' ) ;
2022-02-24 11:52:16 +01:00
resolve ( ) ;
}
} ) ;
2022-02-22 17:00:30 +01:00
}
2022-02-28 01:53:13 +01:00
// led / index helper functions
function getIndices ( blinkstickConfig ) {
2022-03-01 22:14:36 +01:00
if ( blinkstickConfig . options . index === constants . ALL ) {
2022-02-28 01:53:13 +01:00
return [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
}
return [ blinkstickConfig . options . index ] ;
}
2022-02-28 02:14:12 +01:00
async function getColor ( index ) {
if ( index === undefined ) {
index = 0 ;
}
logger . debug ( 'getting color for led with index \'' + index + '\'' ) ;
const blinkstick = await findBlinkstick ( ) ;
return await new Promise ( ( resolve , reject ) => {
2022-03-01 22:14:36 +01:00
blinkstick . getColorString ( index , ( err , color ) => {
if ( err ) {
reject ( err ) ;
}
2022-02-28 02:14:12 +01:00
logger . debug ( 'led with index \'' + index + '\' is set to color \'' + color + '\'' ) ;
resolve ( color ) ;
} ) ;
} ) ;
}
async function setColorIfRandom ( config ) {
2022-03-01 22:14:36 +01:00
if ( config . options . index !== constants . ALL || config . color !== constants . RANDOM ) {
return ;
2022-02-28 02:14:12 +01:00
}
2022-03-01 22:14:36 +01:00
config . color = await getColor ( 0 ) ;
2022-02-28 02:14:12 +01:00
}
2022-02-28 01:53:13 +01:00
async function stopLEDsAccordingly ( config ) {
2022-03-02 00:18:14 +01:00
if ( LEDAnimations . size === 0 && config . mode !== constants . MODE _BLINK && config . mode !== constants . MODE _PULSE ) {
2022-02-28 01:53:13 +01:00
return ;
}
2022-03-01 22:14:36 +01:00
if ( config . options . index === constants . ALL ) {
2022-02-25 22:59:01 +01:00
logger . debug ( 'stopping all leds...' ) ;
2022-02-24 14:53:17 +01:00
return await powerOff ( {
id : Math . random ( ) ,
2022-03-01 22:14:36 +01:00
mode : constants . MODE _POWEROFF ,
2022-02-24 14:53:17 +01:00
color : '#000000' ,
2022-03-01 22:14:36 +01:00
options : { index : constants . ALL }
2022-02-24 14:53:17 +01:00
} ) ;
2022-02-24 14:42:30 +01:00
}
2022-02-28 01:53:13 +01:00
return stopLEDAnimation ( config . options . index ) ;
2022-02-24 14:42:30 +01:00
}
2022-02-28 01:53:13 +01:00
function shouldLEDFinish ( config ) {
2022-03-01 22:14:36 +01:00
if ( LEDAnimations . has ( constants . ALL ) || ( isLEDAnimated ( config . options . index ) && isLEDStopping ( config . options . index ) ) ) {
2022-02-25 22:59:01 +01:00
logger . debug ( 'led \'' + config . options . index + '\' is set to \'stop\' and should finish now' ) ;
2022-02-23 17:08:41 +01:00
return true ;
}
2022-03-01 22:14:36 +01:00
if ( config . mode === constants . MODE _BLINK || config . mode === constants . MODE _PULSE ) {
2022-02-24 14:42:30 +01:00
return config . repetitions . max !== 0 && config . repetitions . done >= config . repetitions . max ;
2022-02-23 17:08:41 +01:00
}
return false ;
}
2022-02-28 01:53:13 +01:00
function setLEDAnimated ( index ) {
if ( isLEDAnimated ( index ) ) {
2022-02-24 14:42:30 +01:00
return ;
}
2022-02-28 01:53:13 +01:00
LEDAnimations . set ( index , { start : new Date ( ) . getTime ( ) } ) ;
2022-02-25 22:59:01 +01:00
logger . debug ( 'led \'' + index + '\ set to \'animated\'' ) ;
2022-02-24 14:42:30 +01:00
}
2022-02-28 01:53:13 +01:00
function setLEDStopping ( index ) {
if ( ! isLEDAnimated ( index ) || isLEDStopping ( index ) ) {
2022-02-24 14:42:30 +01:00
return ;
}
2022-02-28 01:53:13 +01:00
if ( LEDAnimations . has ( index ) ) {
LEDAnimations . get ( index ) . stop = new Date ( ) . getTime ( ) ;
}
2022-02-25 22:59:01 +01:00
logger . debug ( 'led \'' + index + '\ set to \'stop\'' ) ;
2022-02-22 16:42:49 +01:00
}
function clearLedState ( index ) {
2022-03-01 22:14:36 +01:00
if ( index === constants . ALL ) {
2022-02-28 01:53:13 +01:00
LEDAnimations . clear ( ) ;
2022-02-25 22:59:01 +01:00
logger . debug ( 'cleared animation state of all leds' ) ;
2022-02-24 14:42:30 +01:00
return ;
}
2022-02-28 01:53:13 +01:00
LEDAnimations . delete ( index ) ;
2022-02-25 22:59:01 +01:00
logger . debug ( 'cleared animation state of led \'' + index + '\'' ) ;
2022-02-22 16:42:49 +01:00
}
2022-02-28 01:53:13 +01:00
function isLEDAnimated ( index ) {
return LEDAnimations . has ( index ) ;
2022-02-24 14:42:30 +01:00
}
2022-02-28 01:53:13 +01:00
function isLEDStopping ( index ) {
2022-03-01 22:14:36 +01:00
if ( LEDAnimations . has ( constants . ALL ) && LEDAnimations . get ( constants . ALL ) . stop !== undefined ) {
2022-02-28 01:53:13 +01:00
return true ;
}
if ( LEDAnimations . has ( index ) && LEDAnimations . get ( index ) . stop !== undefined ) {
2022-02-24 14:42:30 +01:00
return true ;
}
2022-02-28 01:53:13 +01:00
return false ;
2022-02-22 16:42:49 +01:00
}
2022-02-28 01:53:13 +01:00
async function stopLEDAnimation ( index ) {
2022-03-01 22:14:36 +01:00
if ( index === constants . ALL ) {
2022-02-28 01:53:13 +01:00
for ( const [ key , value ] of LEDAnimations ) {
setLEDStopping ( key ) ;
}
await waitForAllAnimationsEnd ( ) ;
return ;
}
setLEDStopping ( index ) ;
await waitForAnimationEnd ( index ) ;
2022-02-22 16:42:49 +01:00
}
2022-02-28 01:53:13 +01:00
async function waitForAnimationEnd ( index , timestamp ) {
2022-02-25 22:59:01 +01:00
logger . debug ( 'waiting for animated led \'' + index + '\' to end...' ) ;
2022-02-28 01:53:13 +01:00
if ( ! isLEDAnimated ( index ) ) {
2022-02-25 22:59:01 +01:00
logger . debug ( 'animation of led \'' + index + '\' should have ended now' ) ;
2022-02-28 01:53:13 +01:00
return ;
2022-02-22 16:42:49 +01:00
}
2022-02-28 01:53:13 +01:00
if ( timestamp === undefined ) {
timestamp = new Date ( ) . getTime ( ) ;
}
await util . sleep ( 100 ) ;
return await waitForAnimationEnd ( index , timestamp ) ;
2022-02-22 16:42:49 +01:00
}
2022-02-28 01:53:13 +01:00
async function waitForAllAnimationsEnd ( callback , timestamp ) {
if ( LEDAnimations . size === 0 ) {
return ;
}
2022-02-25 22:59:01 +01:00
logger . debug ( 'waiting for all animations to end...' ) ;
2022-02-28 01:53:13 +01:00
for ( const [ key , value ] of LEDAnimations ) {
await waitForAnimationEnd ( key ) ;
2021-04-06 16:41:49 +02:00
}
}
2022-02-23 17:08:41 +01:00
function isInfiniteAnimation ( config ) {
2022-03-01 22:14:36 +01:00
if ( config . mode !== constants . MODE _BLINK && config . mode !== constants . MODE _PULSE ) {
2022-02-23 17:08:41 +01:00
return false ;
2021-04-06 16:41:49 +02:00
}
2022-02-24 11:52:16 +01:00
return config . repetitions . max === 0 ;
2021-04-06 16:41:49 +02:00
}
// exports
module . exports = {
2022-02-25 15:40:20 +01:00
findBlinkstick ,
2022-02-23 17:08:41 +01:00
simple ,
complex ,
2022-02-22 16:42:49 +01:00
powerOff ,
2022-03-01 22:14:36 +01:00
isInfiniteAnimation
}