-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(add_sqlite3_for_persistent_storage): adding sqlite3 for persisten…
…t storage between browsers. -- fixes dump download archive to actually download archive and alert user to not navigate from page until archive finishes. -- adds a view current playlist dialog to assist user of firestorm to quickly see whats in the current playlist being sent to PixelBlazes -- adds brightness slider to Firestorm UI to control brightness on all PixelBlazes in network. -- add SQLite to backend, and send messages to backend via websockets to assist in user expereince verus using fetch to send requests to backend node-server. Node-server on backend will process playlist now. -- add Enable/Disable All buttons to quickly enable all patterns in Firestorm list so a user doesn't have to click through them one by one if they dont want to. -- minor styling issues to better use the bootstrap templating. -- upgraded webstorm to 8.13.0 and modified controller to look at binary messages due to spec changes
- Loading branch information
Showing
18 changed files
with
1,957 additions
and
118 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,3 +19,5 @@ | |
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
|
||
.idea |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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') | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}`) | ||
} | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} | ||
} | ||
} |
Oops, something went wrong.