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-03-04 00:02:07 +01:00
let blinksticks = new Map ( ) ;
2022-02-25 15:40:20 +01:00
2022-03-04 00:02:07 +01:00
// get connected blinkstick by id (or return the first one found)
async function getBlinkstick ( id , filter ) {
if ( ! global . config . blinkstick ? . cache || blinksticks . size === 0 ) {
mapBlinkSticks ( filter ) ;
}
if ( id === undefined ) {
return blinksticks . values ( ) . next ( ) . value ;
}
if ( id === constants . ALL ) {
return Array . from ( blinksticks . values ( ) ) ;
}
if ( ! blinksticks . has ( id ) ) {
throw new Error ( 'could not find any blinkstick matching the given id \'' + id + '\'' ) ;
}
return blinksticks . get ( id ) ;
}
// map found blinksticks
function mapBlinkSticks ( filter ) {
const foundBlinksticks = blinkstick . findAll ( ) ;
if ( filter === undefined ) {
filter = global . config ? . blinkstick ? . map ? . length > 0 ;
}
blinksticks = new Map ( ) ;
filter = filter && global . config . blinkstick ? . map ? . length > 0 ;
for ( let blinkstickIndex = 0 ; blinkstickIndex < foundBlinksticks . length ; blinkstickIndex ++ ) {
const serial = foundBlinksticks [ blinkstickIndex ] . serial ;
if ( ! filter ) {
blinksticks . set ( serial , foundBlinksticks [ blinkstickIndex ] ) ;
continue ;
}
for ( filterIndex = 0 ; filterIndex < global . config . blinkstick . map . length ; filterIndex ++ ) {
let tmp = global . config . blinkstick . map [ filterIndex ] ;
if ( tmp . serial !== serial ) {
continue ;
2022-02-25 15:50:32 +01:00
}
2022-03-04 00:02:07 +01:00
blinksticks . set ( tmp . id || serial , foundBlinksticks [ blinkstickIndex ] ) ;
break ;
2022-02-25 15:50:32 +01:00
}
2022-02-25 15:40:20 +01:00
}
2022-03-07 23:19:18 +01:00
if ( blinksticks . size === 0 ) {
2022-03-04 00:02:07 +01:00
if ( filter ) {
throw new Error ( 'could not find any blinkstick matching the given serial(s)' ) ;
} else {
throw new Error ( 'could not find any blinkstick, make sure at least one blinkstick is connected' ) ;
}
2022-02-25 15:40:20 +01:00
}
2022-03-04 00:02:07 +01:00
}
// reset a blinkstick
async function resetBlinkstick ( id ) {
if ( blinksticks === undefined || blinksticks . length === 0 ) {
return ;
2022-02-24 10:57:41 +01:00
}
2022-03-04 00:02:07 +01:00
let tmp ;
if ( id === constants . ALL ) {
tmp = await getBlinkstick ( id ) ;
for ( let index = 0 ; index < tmp . length ; index ++ ) {
if ( tmp [ index ] === undefined ) {
continue ;
}
tmp [ index ] . close ( ) ;
}
blinksticks . clear ( ) ;
} else {
tmp = await getBlinkstick ( id ) ;
if ( tmp === undefined ) {
return ;
}
tmp . close ( ) ;
blinksticks . delete ( id ) ;
2022-02-24 10:57:41 +01:00
}
2022-03-04 00:02:07 +01:00
mapBlinkSticks ( ) ;
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-07 23:19:18 +01:00
if ( config . blinkstick === constants . ALL ) {
const promises = [ ] ;
const blinkstickNames = Array . from ( blinksticks . keys ( ) ) ;
for ( let index = 0 ; index < blinkstickNames . length ; index ++ ) {
const tmp = JSON . parse ( JSON . stringify ( config ) ) ;
tmp . blinkstick = blinkstickNames [ index ] ;
promises . push ( powerOff ( tmp ) ) ;
}
return await Promise . allSettled ( promises ) ;
}
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-03-04 00:02:07 +01:00
const blinkstick = await getBlinkstick ( ) ;
2022-02-24 11:52:16 +01:00
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 ;
2022-03-04 00:02:07 +01:00
const blinkstick = await getBlinkstick ( config . blinkstick ) ;
2022-02-24 11:52:16 +01:00
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-03-04 00:02:07 +01:00
async function getColor ( config ) {
const index = 0 ;
if ( ! isNaN ( config . options . index ) ) {
index = parseInt ( config . options . index ) ;
2022-02-28 02:14:12 +01:00
}
logger . debug ( 'getting color for led with index \'' + index + '\'' ) ;
2022-03-04 00:02:07 +01:00
const blinkstick = await getBlinkstick ( config . blinkstick ) ;
2022-02-28 02:14:12 +01:00
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-04 00:02:07 +01:00
config . color = await getColor ( config ) ;
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-03-07 23:19:18 +01:00
getBlinkstick ,
2022-03-04 00:02:07 +01:00
mapBlinkSticks ,
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
}