Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*

.idea
94 changes: 94 additions & 0 deletions app/brightness.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
const _ = require("lodash");
const {updateBrightness, getCurrentBrightness} = require("../db/controllers/brightness");
const {discoverPixelBlazes, sendCommand} = require("./pixelBlazeUtils");

let currentBrightness
let pixelBlazeData = []
let pixelBlazeIds = []
init = async () => {
getCurrentBrightness()
.then((brightness) => {
try {
currentBrightness = brightness[0].value
} catch (err) {
console.warn(`Error: ${err}`)
}
})
pixelBlazeData = discoverPixelBlazes()
pixelBlazeIds = _.map(pixelBlazeData, 'id')
}

initInterval = setInterval(init, 100)

class Brightness {
constructor(utils) {
this.utils = utils ? utils : null
}
adjustBrightness = async (brightness) => {
await new Promise((resolve) => {
const tempBrightness = (brightness) ? brightness : currentBrightness
this.delayedSaveBrightness(resolve, tempBrightness)
})
}
delayedSaveBrightness = _.debounce(async (resolve, brightness) => {
sendCommand(pixelBlazeIds, null, brightness)
await this.storeBrightness(brightness);
currentBrightness = brightness
await this.sendBrightnessMessage(currentBrightness)
}, 1000)
getBrightness = async () =>{
await this.sendBrightnessMessage(currentBrightness)
}
storeBrightness = async (brightness) => {
const body = {
value: brightness
}
await updateBrightness(body)
}
sendBrightnessMessage = async (currentBrightness) => {
// skipping this if utils is not initialized due to no websocket connections
if (this.utils) {
await this.utils.broadcastMessage({currentBrightness: currentBrightness})
}
}
}
// Initializing the brightness loop outside the websocket
// because we might not always have a browser open when
// starting/restarting the node-server... it should send
// commands and operate on the brightness w/o the need of an
// active websocket connection
initThis = async () => {
// halting the brightness message until we get it from the db
while (currentBrightness === undefined) {
await new Promise(resolve => {
setTimeout(resolve, 100)
})
}
let initThe = new Brightness()
await initThe.adjustBrightness(currentBrightness)
}
initThis().then(()=>{})


module.exports.BrightnessWebsocketMessageHandler = function (utils) {
const brightness = new Brightness(utils)
this.utils = utils

this.receiveMessage = async function (data) {
let message
try {
message = JSON.parse(data);
} catch (err) {
this.utils.sendError(err)
return
}
if (message.type === 'ADJUST_BRIGHTNESS') {
// console.log('received adjust brightness message!')
await brightness.adjustBrightness(parseFloat(message.brightness))
}
if (message.type === 'GET_CURRENT_BRIGHTNESS') {
// console.log('received get current brightness message!')
await brightness.getBrightness()
}
}
}
4 changes: 2 additions & 2 deletions app/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,12 @@ module.exports = class PixelblazeController {
this.reconectTimeout = setTimeout(this.connect, 1000);
}

handleMessage(msg) {
handleMessage(msg, isBinary) {
this.lastSeen = new Date().getTime();
// console.log("data from " + this.props.id + " at " + this.props.address, typeof msg, msg);

let props = this.props;
if (typeof msg === "string") {
if (!isBinary) {
try {
_.assign(this.props, _.pick(JSON.parse(msg), PROPFIELDS));
} catch (err) {
Expand Down
32 changes: 32 additions & 0 deletions app/firestormWebsocket.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const WebSocketServer = require('ws').Server
const {PlaylistWebSocketMessageHandler} = require('./playlist')
const {Utils} = require('./utils')
const {BrightnessWebsocketMessageHandler} = require("./brightness");

// start FireStorm WebSocket server
const address = '0.0.0.0';
const port = 1890;
const firestormServer = new WebSocketServer({host: address , port: port});
console.log(`Firestorm server is running on ${address}:${port}`);

firestormServer.on('connection', function (connection) {
const utils = new Utils(connection)
const brightnessWebsocketMessageHandler = new BrightnessWebsocketMessageHandler(utils)
const playlistWebSocketMessageHandler = new PlaylistWebSocketMessageHandler(utils)
if(utils.addFirestormClient(connection)) {
return
}
connection.on('message', async function message(data, isBinary) {
const message = isBinary ? data : data.toString();
// console.log(`incoming msg from: ${utils.getFirestormClientBySocket(connection)}, message: ${message}`)
if (await playlistWebSocketMessageHandler.receiveMessage(message)) {
return
}
if (await brightnessWebsocketMessageHandler.receiveMessage(message)) {
return
}
})
connection.on('close', function() {
console.log('closed connection')
})
})
35 changes: 35 additions & 0 deletions app/pixelBlazeUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const _ = require("lodash");
const {discoveries} = require("./discovery");

module.exports.discoverPixelBlazes = () => {
return _.map(discoveries, function (v, k) {
let res = _.pick(v, ['lastSeen', 'address']);
_.assign(res, v.controller.props);
return res;
})
}

module.exports.sendCommand = (pixelBlazeIds, name, brightness) => {
_.each(pixelBlazeIds, async id => {
id = String(id);
let controller = discoveries[id] && discoveries[id].controller;
if (controller) {
let command = null
if(name !== null && name !== undefined) {
command = {
programName: name
}
}
if(brightness !== null && brightness !== undefined){
command = {
brightness: brightness
}
}
if (command) {
await controller.setCommand(command);
} else {
console.log(`No command sent to Pixelblazes command is ${command}`)
}
}
})
}
181 changes: 181 additions & 0 deletions app/playlist.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
const _ = require("lodash");
const {getPlaylistFromDB, addPatternToPlaylist, removeAllPatterns} = require("../db/controllers/playlist");
const {discoverPixelBlazes, sendCommand} = require("./pixelBlazeUtils");

let currentPlaylist = []
let currentRunningPattern = null
let initInterval
let pixelBlazeData = []
let pixelBlazeIds = []
let playlistLoopTimeout
let playlistTimeout

init = async () => {
getPlaylistFromDB()
.then((data) => {
try {
currentPlaylist = [] // resetting current playlist so it doesn't grow to infinity
currentPlaylist.push(...data) // adding new playlist items to list
} catch (err) {
console.warn(`Error: ${err}`)
}
})
.catch('there was an error gathering playlist details')

// gather pixelBlaze data
pixelBlazeData = discoverPixelBlazes()
pixelBlazeIds = _.map(pixelBlazeData, 'id')
}

initInterval = setInterval(init, 100)

class Playlist {
constructor(utils) {
this.utils = utils ? utils : null
}

playlistLoop = async () => {
while(true) {
await new Promise(resolve => {
playlistLoopTimeout = setTimeout(resolve, 100)
});
if(pixelBlazeIds.length) {
await this.iterateOnPlaylist()
}
initInterval = null
playlistLoopTimeout = null
playlistTimeout = null
}
}
iterateOnPlaylist = async () => {
for (let index = 0; index < currentPlaylist.length; index++) {
const pattern = currentPlaylist[index]
await this.delaySendPattern(pattern)
await new Promise(resolve => {
playlistTimeout = setTimeout(resolve, pattern.duration * 1000)
});
}
}
delaySendPattern = async (pattern) => {
await new Promise((resolve) => {
resolve(
this.sendPattern(pattern)
)
})
}
disableAllPatterns = async () => {
await removeAllPatterns()
await this.runPlaylistLoopNow()
}
enableAllPatterns = async (duration) => {
const pixelBlazePatterns = this.gatherPatternData(pixelBlazeData)
const enableAll = new Promise((resolve) => {
_.each(pixelBlazePatterns, pattern => {
pattern['duration'] = duration
let body = {
name: pattern.name,
duration: pattern.duration
}
addPatternToPlaylist(body)
})
resolve();
});
enableAll
.then(() => {
this.runPlaylistLoopNow()
})
}
gatherPatternData = (pixelBlazeData) => {
let groupByPatternName = {};
_.each(pixelBlazeData, d => {
d.name = d.name || "Pixelblaze_" + d.id // set name if missing
_.each(d.programList, p => {
let pb = {
id: d.id,
name: d.name
};
if (groupByPatternName[p.name]) {
groupByPatternName[p.name].push(pb);
} else {
groupByPatternName[p.name] = [pb];
}
})
})
let groups = _.chain(groupByPatternName)
.map((v, k) => ({name: k}))
.sortBy('name')
.value();
return groups
}
getCurrentProgramState = async () => {
let message = {
currentRunningPattern: currentRunningPattern,
currentPlaylist: currentPlaylist
}
await this.sendPlaylistMessage(message)
}
runPlaylistLoopNow = async () => {
clearInterval(initInterval)
clearInterval(playlistTimeout)
clearInterval(playlistLoopTimeout)

await this.playlistLoop()
}
sendPattern = async (pattern) => {
const name = pattern.name
currentRunningPattern = name
sendCommand(pixelBlazeIds, name)
let message = {
currentRunningPattern: name,
currentPlaylist: currentPlaylist
}
await this.sendPlaylistMessage(message)
}
sendPlaylistMessage = async (message) => {
// skipping this if utils is not initialized due to no websocket connections
if(this.utils) {
this.utils.broadcastMessage(message)
}
}

}
// Initializing the playlist loop outside the websocket
// because we might not always have a browser open when
// starting/restarting the node-server... it should send
// commands and operate on the playlist w/o the need of an
// active websocket connection
initThe = new Playlist()
initThe.playlistLoop()
.then(() => {})


module.exports.PlaylistWebSocketMessageHandler = function (utils) {
const playlist = new Playlist(utils)
this.utils = utils

this.receiveMessage = async function (data) {
let message
try {
message = JSON.parse(data);
} catch (err) {
this.utils.sendError(err)
return
}
if (message.type === 'DISABLE_ALL_PATTERNS') {
// console.log('received message to disable all patterns!')
await playlist.disableAllPatterns(message.duration)
}
if (message.type === 'ENABLE_ALL_PATTERNS') {
// console.log('received message to enable all patterns!')
await playlist.enableAllPatterns(message.duration)
}
if (message.type === 'GET_CURRENT_PROGRAM_STATE') {
// console.log('received get current program state message!')
await playlist.getCurrentProgramState()
}
if (message.type === 'LAUNCH_PLAYLIST_NOW') {
// console.log('received launch playlist now message!')
await playlist.runPlaylistLoopNow()
}
}
}
Loading