2022-02-28 01:53:13 +01:00
const logger = require ( './logger.js' ) ;
const util = require ( './util.js' ) ;
2021-04-06 16:41:49 +02:00
const blinkstick = require ( 'blinkstick' ) ;
2022-02-25 15:40:20 +01:00
const ALL = 'all' ;
2021-04-06 16:41:49 +02:00
2022-02-22 17:00:30 +01:00
const MODE _SET = 'set' ;
const MODE _MORPH = 'morph' ;
2022-02-23 17:08:41 +01:00
const MODE _BLINK = 'blink' ;
2022-02-22 17:00:30 +01:00
const MODE _PULSE = 'pulse' ;
const MODE _POWEROFF = 'poweroff' ;
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-02-25 15:50:32 +01:00
if ( global . config . blinkstick ? . serials ? . length > 0 ) {
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-02-25 15:40:20 +01:00
} else if ( index !== ALL ) {
index = parseInt ( index ) ;
}
if ( isNaN ( index ) ) {
index = 0 ;
}
if ( index > blinksticks . length - 1 ) {
throw new Error ( 'there is no blinkstick for index \'' + index + '\'' ) ;
2022-02-24 10:57:41 +01:00
}
2022-02-25 15:40:20 +01:00
if ( index === ALL ) {
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 ( ) ;
let indices = getIndices ( config ) ;
for ( let index = 0 ; index < indices . length ; index ++ ) {
2022-02-28 01:53:13 +01:00
const tmpConfig = JSON . parse ( JSON . stringify ( config ) ) ;
await singleAnimation ( tmpConfig , indices [ index ] ) ;
2022-02-22 17:00:30 +01:00
}
2022-02-24 11:52:16 +01:00
return {
status : 'ok' ,
color : config . color ,
indices : indices ,
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 ( ) ;
}
let indices = getIndices ( config ) ;
for ( let index = 0 ; index < indices . 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 ) ) ;
await singleAnimation ( tmpConfig , indices [ 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 ( ) ;
let indices = getIndices ( config ) ;
2022-02-28 01:53:13 +01:00
if ( config . options . index === ALL ) {
LEDAnimations . set ( ALL , { stop : new Date ( ) . getTime ( ) } ) ;
}
2022-02-24 11:52:16 +01:00
for ( let index = 0 ; index < indices . length ; index ++ ) {
2022-02-28 01:53:13 +01:00
await stopLEDAnimation ( indices [ index ] ) ;
2022-02-24 11:52:16 +01:00
await singleAnimation ( JSON . parse ( JSON . stringify ( config ) ) , indices [ index ] ) ;
logger . info ( 'led \'' + indices [ index ] + '\' powered off' ) ;
2022-02-22 16:42:49 +01:00
}
2022-02-25 15:40:20 +01:00
if ( config . options . index === 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-02-25 22:59:01 +01:00
return { status : 'ok' , indices : indices , 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 ) {
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 ;
}
2022-02-22 17:00:30 +01:00
2022-02-24 11:52:16 +01:00
function callback ( err ) {
2022-02-28 01:53:13 +01:00
if ( config . mode !== MODE _BLINK && config . mode !== MODE _PULSE ) {
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 ) {
if ( blinkstickConfig . options . index === ALL ) {
return [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
}
return [ blinkstickConfig . options . index ] ;
}
async function stopLEDsAccordingly ( config ) {
if ( LEDAnimations . size === 0 ) {
return ;
}
2022-02-25 15:40:20 +01:00
if ( config . options . index === 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 ( ) ,
mode : MODE _POWEROFF ,
color : '#000000' ,
2022-02-25 22:59:01 +01:00
options : { index : 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 ) {
if ( LEDAnimations . has ( 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-02-24 14:42:30 +01:00
if ( config . mode === MODE _BLINK || config . mode === MODE _PULSE ) {
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-02-25 15:40:20 +01:00
if ( index === 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 ) {
if ( LEDAnimations . has ( ALL ) && LEDAnimations . get ( ALL ) . stop !== undefined ) {
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 ) {
if ( index === ALL ) {
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 ) {
if ( config . mode !== MODE _BLINK && config . mode !== MODE _PULSE ) {
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-02-23 17:08:41 +01:00
isInfiniteAnimation ,
2022-02-25 15:40:20 +01:00
ALL ,
2022-02-22 17:00:30 +01:00
MODE _SET ,
MODE _MORPH ,
2022-02-23 17:08:41 +01:00
MODE _BLINK ,
2022-02-22 17:00:30 +01:00
MODE _PULSE ,
MODE _POWEROFF
2021-04-06 16:41:49 +02:00
} ;