diff --git a/.eslintrc.js b/.eslintrc.js index 1fcf706..ff8bfb4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -20,6 +20,6 @@ module.exports = { 'no-use-before-define': 'off', 'space-infix-ops': 'warn', 'no-bitwise': 'off', - 'no-restricted-globals': 'off' + 'no-restricted-globals': 'off', }, }; diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index c092f47..a11f7db 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -42,4 +42,3 @@ jobs: node-version: 18 - run: npm ci - run: npm run release - diff --git a/.gitignore b/.gitignore index c89c18a..b27a74f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ .DS_Store node_modules -.vscode /dist package-lock.json package-lock.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..1d7ac85 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..eb83d0e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + }, + "eslint.validate": ["javascript"] +} diff --git a/index.html b/index.html index 9476233..c9a73ac 100644 --- a/index.html +++ b/index.html @@ -88,14 +88,12 @@ Pin: - - +

No Device Selected

-
diff --git a/main.js b/main.js index b25251d..aa6b406 100644 --- a/main.js +++ b/main.js @@ -1,4 +1,11 @@ -const { app, BrowserWindow, Menu, ipcMain, nativeTheme, dialog } = require('electron'); +const { + app, + BrowserWindow, + Menu, + ipcMain, + nativeTheme, + dialog, +} = require('electron'); const { autoUpdater } = require('electron-updater'); const path = require('path'); @@ -10,7 +17,6 @@ let manualUpdateCheck = false; let menuObj; let mainWindow; - const menuTemplate = [ ...(isMac ? [{ role: 'appMenu' }] : []), { @@ -19,17 +25,17 @@ const menuTemplate = [ { label: 'Clear Saved Data', id: 'resetViews', - click (menuItem, window, event) { + click(menuItem, window, event) { mainWindow.webContents.send('resetViews'); - } + }, }, { label: 'Reload App', - role: 'reload' + role: 'reload', }, { type: 'separator' }, - ...(isMac ? [{ role: 'close' }] : [{ role: 'quit' }]) - ] + ...(isMac ? [{ role: 'close' }] : [{ role: 'quit' }]), + ], }, { role: 'editMenu' }, { @@ -42,31 +48,31 @@ const menuTemplate = [ accelerator: 'CommandOrControl+1', id: 'window1', enabled: true, - click (menuItem, window, event) { - mainWindow.webContents.send('loadSlot',1); - } + click(menuItem, window, event) { + mainWindow.webContents.send('loadSlot', 1); + }, }, { label: 'Arrangement 2', accelerator: 'CommandOrControl+2', id: 'window2', enabled: true, - click (menuItem, window, event) { - mainWindow.webContents.send('loadSlot',2); - } + click(menuItem, window, event) { + mainWindow.webContents.send('loadSlot', 2); + }, }, { label: 'Arrangement 3', accelerator: 'CommandOrControl+3', id: 'window3', enabled: true, - click (menuItem, window, event) { - mainWindow.webContents.send('loadSlot',3); - } + click(menuItem, window, event) { + mainWindow.webContents.send('loadSlot', 3); + }, }, { type: 'separator' }, - { role: 'toggleDevTools' } - ] + { role: 'toggleDevTools' }, + ], }, { label: 'Device', @@ -76,9 +82,9 @@ const menuTemplate = [ accelerator: 'CommandOrControl+F', id: 'deviceSearch', enabled: true, - click (menuItem, window, event) { + click(menuItem, window, event) { mainWindow.webContents.send('searchAll'); - } + }, }, { type: 'separator' }, { @@ -88,23 +94,23 @@ const menuTemplate = [ accelerator: 'CommandOrControl+P', id: 'devicePin', enabled: false, - click (menuItem, window, event) { + click(menuItem, window, event) { mainWindow.webContents.send( 'setActiveDevicePinned', menuItem.checked ); - } + }, }, { label: 'Delete', accelerator: 'CommandOrControl+Backspace', id: 'deviceDelete', enabled: false, - click (menuItem, window, event) { + click(menuItem, window, event) { mainWindow.webContents.send('deleteActive'); - } - } - ] + }, + }, + ], }, { label: 'Help', @@ -112,67 +118,64 @@ const menuTemplate = [ submenu: [ { label: 'About', - role: 'about' + role: 'about', }, { label: 'Check for Updates', - click: ()=>{ + click: () => { // set manual update flag - manualUpdateCheck = true + manualUpdateCheck = true; autoUpdater.checkForUpdates(); - } + }, }, { label: 'Enable Auto Update', - click: ()=>{ - mainWindow.webContents.send('setAutoUpdate',!autoUpdate); - } - } - ] + click: () => { + mainWindow.webContents.send('setAutoUpdate', !autoUpdate); + }, + }, + ], }, ]; - const windowMac = { - width: 1500, - height: 900, - titleBarStyle: 'hiddenInset', - transparent: true, - frame: false, - show: false, - // backgroundColor: "#333333", - vibrancy: 'window', - visualEffectState: 'followWindow', - webPreferences: { - contextIsolation: false, - nodeIntegration: true, - preload: path.join(__dirname, 'preload.js'), - }, -} - -const windowWin = { width: 1500, height: 900, - backgroundColor: "#333333", + titleBarStyle: 'hiddenInset', + transparent: true, + frame: false, + show: false, + // backgroundColor: "#333333", + vibrancy: 'window', + visualEffectState: 'followWindow', webPreferences: { contextIsolation: false, nodeIntegration: true, preload: path.join(__dirname, 'preload.js'), }, -} +}; -if (isWin) -{ - app.setAppUserModelId(app.name); +const windowWin = { + width: 1500, + height: 900, + backgroundColor: '#333333', + webPreferences: { + contextIsolation: false, + nodeIntegration: true, + preload: path.join(__dirname, 'preload.js'), + }, +}; + +if (isWin) { + app.setAppUserModelId(app.name); } const createWindow = () => { - nativeTheme.themeSource = 'dark'; - - if(isMac){ + + if (isMac) { mainWindow = new BrowserWindow(windowMac); - }else{ + } else { mainWindow = new BrowserWindow(windowWin); } @@ -192,7 +195,7 @@ const createWindow = () => { app.whenReady().then(() => { createWindow(); - + app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow(); }); @@ -224,134 +227,134 @@ ipcMain.on('setDevicePin', (event, arg) => { menuObj.getMenuItemById('devicePin').checked = arg; }); - // Autoupdate logic -ipcMain.on('checkForUpdates', (event, arg)=>{ +ipcMain.on('checkForUpdates', (event, arg) => { autoUpdater.checkForUpdates(); -}) +}); + +ipcMain.on('setAutoUpdate', (event, _autoUpdate) => { + autoUpdate = _autoUpdate; -ipcMain.on('setAutoUpdate', (event, _autoUpdate)=>{ - autoUpdate = _autoUpdate - // update menu item for enabling/disabling auto update - menuTemplate[menuTemplate.length-1].submenu[2].label = autoUpdate ? 'Disable Auto Update' : 'Enable Auto Update'; + menuTemplate[menuTemplate.length - 1].submenu[2].label = autoUpdate + ? 'Disable Auto Update' + : 'Enable Auto Update'; menuObj = Menu.buildFromTemplate(menuTemplate); Menu.setApplicationMenu(menuObj); -}) +}); // this can be set to true to bypass the download update dialog and skip straight to install prompt autoUpdater.autoDownload = false; autoUpdater.on('update-available', (updateInfo) => { - // skip prompting to download if autoDownload is set - if(autoUpdater.autoDownload){ + if (autoUpdater.autoDownload) { return; } - const msg = `Version v${updateInfo.version} is available. Would you like to download it?` + const msg = `Version v${updateInfo.version} is available. Would you like to download it?`; const title = 'Update Available'; - let dialogOpts = {} + let dialogOpts = {}; - if(isMac){ + if (isMac) { dialogOpts = { type: 'info', buttons: ['Download', 'Cancel'], title: 'Update Available', message: title, - detail: msg - } - }else{ + detail: msg, + }; + } else { dialogOpts = { type: 'info', buttons: ['Download', 'Cancel'], title: 'Update Available', - message: msg - } + message: msg, + }; } - - dialog.showMessageBox(mainWindow,dialogOpts).then((returnValue) => { + + dialog.showMessageBox(mainWindow, dialogOpts).then((returnValue) => { // download was clicked - if (returnValue.response === 0){ + if (returnValue.response === 0) { autoUpdater.downloadUpdate(); } - }) -}) + }); +}); -autoUpdater.on('update-downloaded',(event)=>{ +autoUpdater.on('update-downloaded', (event) => { const title = 'Update Downloaded'; - const msg = `Version v${event.version} has been downloaded. Would you like to install this update now?` + const msg = `Version v${event.version} has been downloaded. Would you like to install this update now?`; - let dialogOpts = {} - - if(isMac){ + let dialogOpts = {}; + + if (isMac) { dialogOpts = { type: 'info', buttons: ['Install', 'Later'], title: 'Update Available', message: title, - detail: msg - } - }else{ + detail: msg, + }; + } else { dialogOpts = { type: 'info', buttons: ['Install', 'Later'], title: 'Update Available', - message: msg - } + message: msg, + }; } - - dialog.showMessageBox(mainWindow,dialogOpts).then((returnValue) => { - if (returnValue.response === 0) autoUpdater.quitAndInstall() - }) -}) -autoUpdater.on('update-not-available',(updateInfo)=>{ - if(manualUpdateCheck){ - let dialogOpts = {} - const msg = `There is no update available at this time. Latest version is v${updateInfo.version}` - if(isMac){ + dialog.showMessageBox(mainWindow, dialogOpts).then((returnValue) => { + if (returnValue.response === 0) autoUpdater.quitAndInstall(); + }); +}); + +autoUpdater.on('update-not-available', (updateInfo) => { + if (manualUpdateCheck) { + let dialogOpts = {}; + const msg = `There is no update available at this time. Latest version is v${updateInfo.version}`; + if (isMac) { dialogOpts = { type: 'info', buttons: ['Ok'], title: 'No Update Available', message: 'No Update Available', - detail: msg - } - }else{ + detail: msg, + }; + } else { dialogOpts = { type: 'info', buttons: ['Ok'], title: 'No Update Available', - message: msg - } + message: msg, + }; } - dialog.showMessageBox(mainWindow,dialogOpts) + dialog.showMessageBox(mainWindow, dialogOpts); // revert manual update flag manualUpdateCheck = false; } -}) +}); -autoUpdater.on('error',(error,message)=>{ - let dialogOpts = {} - - if(isMac){ +autoUpdater.on('error', (error, message) => { + let dialogOpts = {}; + + if (isMac) { dialogOpts = { type: 'error', buttons: ['Ok'], title: 'Update Error', message: 'Update Error', - detail: error.message - } - }else{ + detail: error.message, + }; + } else { dialogOpts = { type: 'error', buttons: ['Ok'], title: 'Update Error', - message: error.message - } + message: error.message, + }; } - dialog.showMessageBox(mainWindow,dialogOpts) -}) + dialog.showMessageBox(mainWindow, dialogOpts); +}); diff --git a/notarize.js b/notarize.js index 418b34a..6d8136a 100644 --- a/notarize.js +++ b/notarize.js @@ -1,41 +1,44 @@ const notarize = require('@electron/notarize'); -const fs = require('fs') -const path = require('path') +const fs = require('fs'); +const path = require('path'); module.exports = async function (params) { - if (process.platform !== 'darwin') { - console.log('Only need to notarize MacOS, skipping') - return - } - - const appId = 'com.stagehacks.cueview' + if (process.platform !== 'darwin') { + console.log('Only need to notarize MacOS, skipping'); + return; + } - const appPath = path.join( - params.appOutDir, - `${params.packager.appInfo.productFilename}.app` - ) + const appId = 'com.stagehacks.cueview'; - if (!fs.existsSync(appPath)) { - console.error('App file not found skipping notarization.'); - return; - } - - if(process.env.APPLE_ID === undefined || process.env.APPLE_ID_PASSWORD === undefined){ - console.log('Apple ID and Password must be set in order to notarize.') - return; - } + const appPath = path.join( + params.appOutDir, + `${params.packager.appInfo.productFilename}.app` + ); - try { - console.log(`Notarizing ${appId} found at ${appPath}`) - await notarize.notarize({ - appBundleId: appId, - appPath, - appleId: process.env.APPLE_ID, - appleIdPassword: process.env.APPLE_ID_PASSWORD, - }) - console.log(`Done notarizing ${appId}`) - } catch (error) { - console.log('There was an error notarizing.') - console.error(error) - } -} \ No newline at end of file + if (!fs.existsSync(appPath)) { + console.error('App file not found skipping notarization.'); + return; + } + + if ( + process.env.APPLE_ID === undefined || + process.env.APPLE_ID_PASSWORD === undefined + ) { + console.log('Apple ID and Password must be set in order to notarize.'); + return; + } + + try { + console.log(`Notarizing ${appId} found at ${appPath}`); + await notarize.notarize({ + appBundleId: appId, + appPath, + appleId: process.env.APPLE_ID, + appleIdPassword: process.env.APPLE_ID_PASSWORD, + }); + console.log(`Done notarizing ${appId}`); + } catch (error) { + console.log('There was an error notarizing.'); + console.error(error); + } +}; diff --git a/plugins/artnet/info.html b/plugins/artnet/info.html index 4284f4e..0ccf028 100644 --- a/plugins/artnet/info.html +++ b/plugins/artnet/info.html @@ -1,2 +1,2 @@

Art-Net requires no configuration.

-

Art-Netâ„¢ Designed by and Copyright Artistic Licence Holdings Ltd

\ No newline at end of file +

Art-Netâ„¢ Designed by and Copyright Artistic Licence Holdings Ltd

diff --git a/plugins/artnet/main.js b/plugins/artnet/main.js index e897d7a..7306394 100644 --- a/plugins/artnet/main.js +++ b/plugins/artnet/main.js @@ -1,8 +1,8 @@ const _ = require('lodash'); exports.config = { - defaultName: "Art-Net", - connectionType: "UDPsocket", + defaultName: 'Art-Net', + connectionType: 'UDPsocket', heartbeatInterval: 10000, searchOptions: { type: 'UDPsocket', @@ -10,10 +10,10 @@ exports.config = { devicePort: 6454, listenPort: 6454, mayChangePort: false, - validateResponse (msg, info, devices) { - return msg.toString('utf8', 0, 7) === "Art-Net"; - } - } + validateResponse(msg, info, devices) { + return msg.toString('utf8', 0, 7) === 'Art-Net'; + }, + }, }; exports.defaultPort = 6454; @@ -25,14 +25,14 @@ exports.ready = function ready(_device) { }; exports.data = function data(_device, buf) { - if(buf.length < 18){ - return + if (buf.length < 18) { + return; } const universeIndex = buf.readUInt8(14); const device = _device; let universe = device.data.universes[universeIndex]; - if(!universe){ + if (!universe) { device.data.universes[universeIndex] = {}; universe = device.data.universes[universeIndex]; } @@ -41,10 +41,10 @@ exports.data = function data(_device, buf) { universe.subnet = buf.readUInt8(15); universe.opCode = buf.readUInt8(9); universe.version = buf.readUInt16BE(10); - universe.slots = buf.slice(18) + universe.slots = buf.slice(18); device.data.ip = device.addresses[0]; - if(!_.includes(device.data.orderedUniverses, universeIndex)){ + if (!_.includes(device.data.orderedUniverses, universeIndex)) { device.data.orderedUniverses.push(universeIndex); device.data.orderedUniverses.sort(); universe.slotElems = []; @@ -54,49 +54,47 @@ exports.data = function data(_device, buf) { device.update('elementCache'); } - device.update("universeData", { + device.update('universeData', { universeIndex, - universe + universe, }); }; -exports.heartbeat = function heartbeat(device) { - -}; +exports.heartbeat = function heartbeat(device) {}; let lastUpdate = Date.now(); -exports.update = function update(_device, _document, updateType, updateData){ - const device = _device +exports.update = function update(_device, _document, updateType, updateData) { + const device = _device; const data = updateData; const document = _document; - if(updateType === "universeData" && data.universe){ - - if(Date.now() - lastUpdate > 1000){ + if (updateType === 'universeData' && data.universe) { + if (Date.now() - lastUpdate > 1000) { lastUpdate = Date.now(); - device.update("elementCache"); + device.update('elementCache'); } const $elem = document.getElementById(`universe-${data.universeIndex}`); - if($elem && data.universe.slotElemsSet){ - for(let i = 0; i < 512; i++){ + if ($elem && data.universe.slotElemsSet) { + for (let i = 0; i < 512; i++) { data.universe.slotElems[i].innerText = data.universe.slots[i]; } - document.getElementById(`universe-${data.universeIndex}-sequence`).innerText = data.universe.sequence; - }else{ + document.getElementById( + `universe-${data.universeIndex}-sequence` + ).innerText = data.universe.sequence; + } else { device.draw(); - device.update("elementCache"); + device.update('elementCache'); } - - }else if(updateType === "elementCache"){ - device.data.orderedUniverses.forEach(universeIndex => { - for(let i = 0; i < 512; i++){ - device.data.universes[universeIndex].slotElems[i] = document.getElementById(`${universeIndex}-${i}`); + } else if (updateType === 'elementCache') { + device.data.orderedUniverses.forEach((universeIndex) => { + for (let i = 0; i < 512; i++) { + device.data.universes[universeIndex].slotElems[i] = + document.getElementById(`${universeIndex}-${i}`); } device.data.universes[universeIndex].slotElemsSet = true; }); } -} - +}; diff --git a/plugins/eos/info.html b/plugins/eos/info.html index bd6e1c1..4cb5ec9 100644 --- a/plugins/eos/info.html +++ b/plugins/eos/info.html @@ -2,14 +2,17 @@

Shell/ECU → Network

-If the "Third Party OSC" option is not available, Eos must be updated to 3.1 or newer. +If the "Third Party OSC" option is not available, Eos must be updated to 3.1 or +newer.

System Settings → Show Control → OSC

\ No newline at end of file +
  • Enable ✔ OSC RX
  • +
  • Enable ✔ OSC TX
  • +
  • All other fields may be left to defaults or blank.
  • + diff --git a/plugins/eos/main.js b/plugins/eos/main.js index e9a9f64..d42dcde 100644 --- a/plugins/eos/main.js +++ b/plugins/eos/main.js @@ -4,8 +4,8 @@ const path = require('path'); const Cue = require('./cue'); exports.config = { - defaultName: "ETC Eos", - connectionType: "osc", + defaultName: 'ETC Eos', + connectionType: 'osc', defaultPort: 3032, mayChangePort: false, searchOptions: { @@ -17,9 +17,9 @@ exports.config = { testPort: 3032, validateResponse(msg, info) { return msg.toString().indexOf('/eos/out'); - } - } -} + }, + }, +}; exports.ready = function ready(_device) { const device = _device; @@ -53,7 +53,18 @@ exports.data = function data(_device, osc) { device.send(`/eos/get/cue/${addressParts[4]}/index/${i}`); } } else if ( - match(addressParts, [ 'eos', 'out', 'get','cue', '*', '*', '*', 'list', '*', '*',]) + match(addressParts, [ + 'eos', + 'out', + 'get', + 'cue', + '*', + '*', + '*', + 'list', + '*', + '*', + ]) ) { this.deviceInfoUpdate(device, 'status', 'ok'); if ( @@ -76,7 +87,19 @@ exports.data = function data(_device, osc) { delete device.data.EOS.cueLists[addressParts[4]][addressParts[5]]; device.draw(); } else if ( - match(addressParts, ['eos', 'out', 'get', 'cue', '*', '*', '*', 'actions', 'list', '*', '*']) + match(addressParts, [ + 'eos', + 'out', + 'get', + 'cue', + '*', + '*', + '*', + 'actions', + 'list', + '*', + '*', + ]) ) { if (osc.args.length === 3) { device.data.EOS.cueLists[addressParts[4]][addressParts[5]][0].extLinks = @@ -130,13 +153,13 @@ exports.heartbeat = function heartbeat(device) { device.send('/eos/ping'); }; -function match(testArray, patternArray){ +function match(testArray, patternArray) { let out = true; - if(testArray.length !== patternArray.length){ + if (testArray.length !== patternArray.length) { return false; } - patternArray.forEach((patternPart, i)=> { - if(testArray[i] !== patternPart && patternPart !== "*"){ + patternArray.forEach((patternPart, i) => { + if (testArray[i] !== patternPart && patternPart !== '*') { out = false; } }); diff --git a/plugins/pjlink/info.html b/plugins/pjlink/info.html index edbdc3c..4b60e01 100644 --- a/plugins/pjlink/info.html +++ b/plugins/pjlink/info.html @@ -1,4 +1,4 @@

    Connection Requirements

    \ No newline at end of file +
  • Requires no password on the projector
  • + diff --git a/plugins/pjlink/main.js b/plugins/pjlink/main.js index c6a7f01..ac293eb 100644 --- a/plugins/pjlink/main.js +++ b/plugins/pjlink/main.js @@ -2,7 +2,7 @@ const md5 = require('md5'); exports.config = { defaultName: 'PJLink Projector', - connectionType: "TCPsocket", + connectionType: 'TCPsocket', heartbeatInterval: 5000, heartbeatTimeout: 15000, defaultPort: 4352, @@ -12,28 +12,29 @@ exports.config = { searchBuffer: Buffer.from([0x25, 0x32, 0x53, 0x52, 0x43, 0x48, 0x0d]), devicePort: 4352, listenPort: 4352, - validateResponse (msg, info) { + validateResponse(msg, info) { console.log(msg.toString()); return msg.toString().indexOf('%2ACKN=') >= 0; - } + }, }, - fields: [{ - key: "password", - label: "Pass", - type: "textinput", - value: "", - action: function(device){ - device.plugin.heartbeat(device); - } - }] -} + fields: [ + { + key: 'password', + label: 'Pass', + type: 'textinput', + value: '', + action(device) { + device.plugin.heartbeat(device); + }, + }, + ], +}; exports.ready = function ready(device) { // Power status query // device.send("%1POWR ?\r"); }; - const PJLinkCmds = [ '%1POWR=', '%1INPT=', @@ -44,8 +45,8 @@ const PJLinkCmds = [ '%1INF1=', '%1INF2=', '%2SNUM=', - '%2SVER=' -] + '%2SVER=', +]; let passwordMD5 = false; let passwordSeed = false; @@ -114,44 +115,41 @@ function processPJLink(_device, str, that) { device.draw(); } -exports.data = function data(device, message) { +exports.data = function data(_device, message) { this.deviceInfoUpdate(device, 'status', 'ok'); const msg = message.toString(); - - //console.log(msg); + const device = _device; if (msg.substring(0, 8) === 'PJLINK 1') { passwordSeed = msg.substring(9, 17); passwordMD5 = md5(`${passwordSeed}${device.fields.password}`); - device.data.authentication = "ON"; + device.data.authentication = 'ON'; device.send( `${passwordMD5}%1POWR ?\r%1INPT ?\r%1AVMT ?\r%1ERST ?\r%1LAMP ?\r%1NAME ?\r%1INF1 ?\r%1INF2 ?\r%2SNUM ?\r%2SVER ?\r` ); device.draw(); - - }else if(msg.substring(0, 8) === 'PJLINK 0') { - device.data.authentication = "OFF"; + } else if (msg.substring(0, 8) === 'PJLINK 0') { + device.data.authentication = 'OFF'; device.draw(); - - }else if(msg.startsWith('PJLINK ERRA')) { + } else if (msg.startsWith('PJLINK ERRA')) { device.data.passwordOK = false; device.draw(); } - if(PJLinkCmds.includes(msg.substring(0,7))){ - processPJLink(device,msg,this); + if (PJLinkCmds.includes(msg.substring(0, 7))) { + processPJLink(device, msg, this); device.data.passwordOK = true; } }; exports.heartbeat = function heartbeat(device) { passwordMD5 = md5(`${passwordSeed}${device.fields.password}`); - if (device.fields.password.length>0) { + if (device.fields.password.length > 0) { device.send( `${passwordMD5}%1POWR ?\r%1INPT ?\r%1AVMT ?\r%1ERST ?\r%1LAMP ?\r%1NAME ?\r%1INF1 ?\r%1INF2 ?\r%2SNUM ?\r%2SVER ?\r` ); - }else{ + } else { device.send( `%1POWR ?\r%1INPT ?\r%1AVMT ?\r%1ERST ?\r%1LAMP ?\r%1NAME ?\r%1INF1 ?\r%1INF2 ?\r%2SNUM ?\r%2SVER ?\r` ); diff --git a/plugins/pjlink/styles.css b/plugins/pjlink/styles.css index 0ed35ba..970f403 100644 --- a/plugins/pjlink/styles.css +++ b/plugins/pjlink/styles.css @@ -7,7 +7,7 @@ .ok { color: #79b757; } -table{ +table { margin-bottom: 30px; width: 350px; } diff --git a/plugins/qlab/info.html b/plugins/qlab/info.html index d3ef67f..fe4a8d0 100644 --- a/plugins/qlab/info.html +++ b/plugins/qlab/info.html @@ -1,5 +1,5 @@

    Connection Requirements

    \ No newline at end of file +
  • View permission enabled in OSC Access
  • +
  • Provide passcode if necessary
  • + diff --git a/plugins/qlab/main.js b/plugins/qlab/main.js index a57663c..67d61f3 100644 --- a/plugins/qlab/main.js +++ b/plugins/qlab/main.js @@ -3,8 +3,8 @@ const fs = require('fs'); const path = require('path'); exports.config = { - defaultName: "QLab", - connectionType: "osc", + defaultName: 'QLab', + connectionType: 'osc', heartbeatInterval: 50, heartbeatTimeout: 2000, mayChangePort: true, @@ -12,31 +12,38 @@ exports.config = { type: 'Bonjour', bonjourName: 'qlab', }, - fields: [{ - key: "passcode", - label: "Pass", - type: "textinput", - value: "", - action: function(device){ - device.send('/workspaces'); - } - }] -} + fields: [ + { + key: 'passcode', + label: 'Pass', + type: 'textinput', + value: '', + action(device) { + device.send('/workspaces'); + }, + }, + ], +}; let lastElapsedUpdate = Date.now(); let interval = 5; let heartbeatCount = 0; -const valuesForKeysString = -'["uniqueID","number","name","listName","isBroken","isRunning","isLoaded","isFlagged",' -+ '"type","children","preWait","postWait","currentDuration","colorName","continueMode",' -+ '"mode","parent","cartRows","cartColumns","cartPosition","displayName","preWaitElapsed",' -+ '"actionElapsed","postWaitElapsed","isPaused"]'; - -const cueTemplate = _.template(fs.readFileSync(path.join(__dirname, `cue.ejs`))); -const tileTemplate = _.template(fs.readFileSync(path.join(__dirname, `tile.ejs`))); -const cartTemplate = _.template(fs.readFileSync(path.join(__dirname, `cart.ejs`))); +const valuesForKeysString = + '["uniqueID","number","name","listName","isBroken","isRunning","isLoaded","isFlagged",' + + '"type","children","preWait","postWait","currentDuration","colorName","continueMode",' + + '"mode","parent","cartRows","cartColumns","cartPosition","displayName","preWaitElapsed",' + + '"actionElapsed","postWaitElapsed","isPaused"]'; +const cueTemplate = _.template( + fs.readFileSync(path.join(__dirname, `cue.ejs`)) +); +const tileTemplate = _.template( + fs.readFileSync(path.join(__dirname, `tile.ejs`)) +); +const cartTemplate = _.template( + fs.readFileSync(path.join(__dirname, `cart.ejs`)) +); exports.ready = function ready(device) { device.send(`/version`); @@ -49,36 +56,34 @@ exports.data = function data(_device, oscData) { const oscAddressParts = oscData.address.split('/'); oscAddressParts.shift(); - if(match(oscAddressParts, ["reply", "version"])){ + if (match(oscAddressParts, ['reply', 'version'])) { const json = JSON.parse(oscData.args[0]); device.data.version = json.data; this.deviceInfoUpdate(device, 'status', 'ok'); - }else if(match(oscAddressParts, ["reply", "workspaces"])){ - + } else if (match(oscAddressParts, ['reply', 'workspaces'])) { const json = JSON.parse(oscData.args[0]); device.data.workspaces = {}; - json.data.forEach(wksp => { - + json.data.forEach((wksp) => { device.data.workspaces[wksp.uniqueID] = { uniqueID: wksp.uniqueID, displayName: wksp.displayName, cueLists: {}, - cues: {} - } - device.send(`/workspace/${wksp.uniqueID}/connect`, device.fields.passcode); + cues: {}, + }; + device.send( + `/workspace/${wksp.uniqueID}/connect`, + device.fields.passcode + ); }); - }else if(match(oscAddressParts, ["reply", "workspace", "*", "connect"])){ - + } else if (match(oscAddressParts, ['reply', 'workspace', '*', 'connect'])) { device.send(`/workspace/${oscAddressParts[2]}/updates`, [ { type: 'i', value: 1 }, ]); device.send(`/workspace/${oscAddressParts[2]}/cueLists`); - - }else if(match(oscAddressParts, ["reply", "workspace", "*", "cueLists"])){ - + } else if (match(oscAddressParts, ['reply', 'workspace', '*', 'cueLists'])) { this.deviceInfoUpdate(device, 'status', 'ok'); const workspace = device.data.workspaces[oscAddressParts[2]]; const json = JSON.parse(oscData.args[0]); @@ -86,13 +91,12 @@ exports.data = function data(_device, oscData) { workspace.cueLists = {}; workspace.cues = {}; - if(json.status=="denied"){ + if (json.status === 'denied') { device.data.permission = false; - - }else if(json.data){ + } else if (json.data) { device.data.permission = true; - json.data.forEach(cueList => { + json.data.forEach((cueList) => { workspace.cueLists[cueList.uniqueID] = cueList; addCueToWorkspace(workspace, cueList); }); @@ -100,35 +104,51 @@ exports.data = function data(_device, oscData) { device.draw(); setTimeout(() => { - json.data.forEach(ql => { + json.data.forEach((ql) => { workspace.cueLists[ql.uniqueID] = ql; getValuesForKeys(device, json.workspace_id, ql); }); }, 0); } - - }else if(match(oscAddressParts, ["reply", "cue_id", "*", "children"]) || match(oscAddressParts, ["reply", "workspace", "*", "cue_id", "*", "children"])){ - + } else if ( + match(oscAddressParts, ['reply', 'cue_id', '*', 'children']) || + match(oscAddressParts, [ + 'reply', + 'workspace', + '*', + 'cue_id', + '*', + 'children', + ]) + ) { const json = JSON.parse(oscData.args[0]); const workspace = device.data.workspaces[json.workspace_id]; const cueID = json.address.substring(55, 91); const cue = workspace.cues[cueID]; - if(!_.isEqual(cue.cues, json.data)){ + if (!_.isEqual(cue.cues, json.data)) { workspace.cueLists[cueID].cues = json.data; addCueToWorkspace(workspace, workspace.cueLists[cueID]); device.draw(); getValuesForKeys(device, json.workspace_id, cue); } - - }else if(match(oscAddressParts, ["reply", "cue_id", "*", "valuesForKeys"]) || match(oscAddressParts, ["reply", "workspace", "*", "cue_id", "*", "valuesForKeys"])){ - + } else if ( + match(oscAddressParts, ['reply', 'cue_id', '*', 'valuesForKeys']) || + match(oscAddressParts, [ + 'reply', + 'workspace', + '*', + 'cue_id', + '*', + 'valuesForKeys', + ]) + ) { const json = JSON.parse(oscData.args[0]); const cueValues = json.data; const workspace = device.data.workspaces[json.workspace_id]; let cue = workspace.cues[cueValues.uniqueID]; - if(!cue){ + if (!cue) { workspace.cues[cueValues.uniqueID] = {}; cue = workspace.cues[cueValues.uniqueID]; } @@ -144,7 +164,7 @@ exports.data = function data(_device, oscData) { cue.flagged = cueValues.isFlagged; cue.paused = cueValues.isPaused; cue.type = cueValues.type; - //cue.cues = cueValues.children; + // cue.cues = cueValues.children; cue.preWait = cueValues.preWait; cue.postWait = cueValues.postWait; cue.duration = cueValues.currentDuration; @@ -160,52 +180,55 @@ exports.data = function data(_device, oscData) { cue.postWaitElapsed = cueValues.postWaitElapsed; // QLab 5 fix - if(cueValues.type=="Group" || cueValues.type=="Cue List"){ - cue.cues = cueValues.children; - }else{ - cue.cues = undefined; + if (cueValues.type === 'Group' || cueValues.type === 'Cue List') { + cue.cues = cueValues.children; + } else { + cue.cues = undefined; } const nestedGroupModes = []; const nestedGroupPosition = []; - let obj = cue; let sum = 0; - if(obj.cues){ - sum+=obj.cues.length; + if (obj.cues) { + sum += obj.cues.length; } - while(obj.parent !== "[root group of cue lists]"){ + while (obj.parent !== '[root group of cue lists]') { + let pos = _.findIndex(workspace.cues[obj.parent].cues, { + uniqueID: obj.uniqueID, + }); + pos = Math.abs(pos - workspace.cues[obj.parent].cues.length) - 1; - let pos = _.findIndex(workspace.cues[obj.parent].cues, {uniqueID: obj.uniqueID}); - pos = Math.abs(pos - workspace.cues[obj.parent].cues.length)-1; - - if(obj.cues === undefined){ + if (obj.cues === undefined) { sum += pos; } nestedGroupPosition.unshift(sum); - if(obj.cues){ - sum+=pos; + if (obj.cues) { + sum += pos; nestedGroupModes.unshift(obj.groupMode); - }else{ + } else { nestedGroupModes.unshift(workspace.cues[obj.parent].groupMode); } - - obj = workspace.cues[obj.parent]; + obj = workspace.cues[obj.parent]; } cue.nestedGroupModes = nestedGroupModes; cue.nestedGroupPosition = nestedGroupPosition; - device.update("updateCueData", {'cue': cue, 'allCues': workspace.cues, 'workspace': workspace}); - - }else if(match(oscAddressParts, ["reply", "cue_id", "*", "preWaitElapsed"])){ - + device.update('updateCueData', { + cue, + allCues: workspace.cues, + workspace, + }); + } else if ( + match(oscAddressParts, ['reply', 'cue_id', '*', 'preWaitElapsed']) + ) { const json = JSON.parse(oscData.args[0]); const workspace = device.data.workspaces[json.workspace_id]; const cue = workspace.cues[json.address.substring(55, 91)]; @@ -213,10 +236,14 @@ exports.data = function data(_device, oscData) { cue.preWaitElapsed = json.data; lastElapsedUpdate = Date.now(); - device.update("updateCueData", {'cue': cue, 'allCues': workspace.cues, 'workspace': workspace}); - - }else if(match(oscAddressParts, ["reply", "cue_id", "*", "actionElapsed"])){ - + device.update('updateCueData', { + cue, + allCues: workspace.cues, + workspace, + }); + } else if ( + match(oscAddressParts, ['reply', 'cue_id', '*', 'actionElapsed']) + ) { const json = JSON.parse(oscData.args[0]); const workspace = device.data.workspaces[json.workspace_id]; const cue = workspace.cues[json.address.substring(55, 91)]; @@ -224,10 +251,14 @@ exports.data = function data(_device, oscData) { cue.actionElapsed = json.data; lastElapsedUpdate = Date.now(); - device.update("updateCueData", {'cue': cue, 'allCues': workspace.cues, 'workspace': workspace}); - - }else if(match(oscAddressParts, ["reply", "cue_id", "*", "postWaitElapsed"])){ - + device.update('updateCueData', { + cue, + allCues: workspace.cues, + workspace, + }); + } else if ( + match(oscAddressParts, ['reply', 'cue_id', '*', 'postWaitElapsed']) + ) { const json = JSON.parse(oscData.args[0]); const workspace = device.data.workspaces[json.workspace_id]; const cue = workspace.cues[json.address.substring(55, 91)]; @@ -235,158 +266,163 @@ exports.data = function data(_device, oscData) { cue.postWaitElapsed = json.data; lastElapsedUpdate = Date.now(); - device.update("updateCueData", {'cue': cue, 'allCues': workspace.cues, 'workspace': workspace}); - - }else if(match(oscAddressParts, ["update", "workspace", "*", "cue_id", "*"])){ - + device.update('updateCueData', { + cue, + allCues: workspace.cues, + workspace, + }); + } else if ( + match(oscAddressParts, ['update', 'workspace', '*', 'cue_id', '*']) + ) { const workspace = device.data.workspaces[oscAddressParts[2]]; - - if(workspace){ + + if (workspace) { const cueLists = Object.keys(workspace.cueLists); const cueID = oscAddressParts[4]; - if(cueID !== "[root group of cue lists"){ - if(cueLists.includes(cueID)){ - device.send(`/workspace/${workspace.uniqueID}/cue_id/${cueID}/children`); + if (cueID !== '[root group of cue lists') { + if (cueLists.includes(cueID)) { + device.send( + `/workspace/${workspace.uniqueID}/cue_id/${cueID}/children` + ); } - device.send(`/workspace/${oscAddressParts[2]}/cue_id/${cueID}/valuesForKeys`, [ - {type: 's', value: valuesForKeysString} - ]); + device.send( + `/workspace/${oscAddressParts[2]}/cue_id/${cueID}/valuesForKeys`, + [{ type: 's', value: valuesForKeysString }] + ); } } - - }else if(match(oscAddressParts, ["update", "workspace", "*"])){ + } else if (match(oscAddressParts, ['update', 'workspace', '*'])) { // occurs when cue lists are reordered or a list is deleted device.send(`/workspace/${oscAddressParts[2]}/cueLists`); - - }else if(match(oscAddressParts, ["update", "workspace", "*", "dashboard"])){ + } else if ( + match(oscAddressParts, ['update', 'workspace', '*', 'dashboard']) + ) { // this workspace might be new, let's check - if(device.data.workspaces[oscAddressParts[2]] === undefined){ - console.log("new workspace!"); + if (device.data.workspaces[oscAddressParts[2]] === undefined) { + console.log('new workspace!'); device.send('/workspaces'); } - - }else if(match(oscAddressParts, ["update", "workspace", "*", "cueList", "*", "playbackPosition"])){ - + } else if ( + match(oscAddressParts, [ + 'update', + 'workspace', + '*', + 'cueList', + '*', + 'playbackPosition', + ]) + ) { const workspace = device.data.workspaces[oscAddressParts[2]]; - if(workspace){ + if (workspace) { const cue = workspace.cues[oscData.args[0]]; - - if(cue){ - workspace.playbackPosition = oscData.args[0] ? cue.uniqueID : ""; - device.update("updatePlaybackPosition", {'cue': cue}); + + if (cue) { + workspace.playbackPosition = oscData.args[0] ? cue.uniqueID : ''; + device.update('updatePlaybackPosition', { cue }); } } - - }else if(match(oscAddressParts, ["update", "workspace", "*", "disconnect"])){ - + } else if ( + match(oscAddressParts, ['update', 'workspace', '*', 'disconnect']) + ) { delete device.data.workspaces[oscAddressParts[2]]; device.draw(); - - }else{ + } else { // console.log(address) } - }; - - - -exports.update = function update(device, doc, updateType, data){ - - if(updateType === "updateCueData"){ +exports.update = function update(device, doc, updateType, data) { + if (updateType === 'updateCueData') { const $elem = doc.getElementById(data.cue.uniqueID); - if($elem){ - - if(data.cue.type === "Cue List"){ + if ($elem) { + if (data.cue.type === 'Cue List') { $elem.outerHTML = `

    ${data.workspace.displayName} — ${data.cue.name}

    `; - - }else if(data.cue.type === "Cart"){ - $elem.outerHTML = cartTemplate({'tileTemplate': tileTemplate, 'cueList': data.cue, 'allCues': data.workspace.cues}); - - }else if(data.cue.cartPosition && data.cue.cartPosition[0] !== 0){ + } else if (data.cue.type === 'Cart') { + $elem.outerHTML = cartTemplate({ + tileTemplate, + cueList: data.cue, + allCues: data.workspace.cues, + }); + } else if (data.cue.cartPosition && data.cue.cartPosition[0] !== 0) { // checking that the parent cue is a cart cue const parentCue = data.workspace.cues[data.cue.parent]; - if(parentCue && parentCue.type === "Cart"){ + if (parentCue && parentCue.type === 'Cart') { $elem.outerHTML = tileTemplate(data); - }else{ + } else { $elem.outerHTML = cueTemplate(data); - } - - }else{ + } + } else { $elem.outerHTML = cueTemplate(data); } } - - }else if(updateType === "updatePlaybackPosition"){ - Array.from(doc.getElementsByClassName("playback-position")).forEach( - ($elem, index, array) => { - $elem.classList.remove("playback-position"); - }); + } else if (updateType === 'updatePlaybackPosition') { + Array.from(doc.getElementsByClassName('playback-position')).forEach( + ($elem, index, array) => { + $elem.classList.remove('playback-position'); + } + ); const $elem = doc.getElementById(data.cue.uniqueID); - $elem.classList.add("playback-position"); - $elem.scrollIntoView({behavior: "smooth", block: "center"}); - + $elem.classList.add('playback-position'); + $elem.scrollIntoView({ behavior: 'smooth', block: 'center' }); } -} +}; - - -function addCueToWorkspace(_workspace, cue){ +function addCueToWorkspace(_workspace, cue) { const workspace = _workspace; workspace.cues[cue.uniqueID] = cue; workspace.cues[cue.uniqueID].nestedGroupModes = []; workspace.cues[cue.uniqueID].nestedGroupPosition = []; - if(cue.cues){ + if (cue.cues) { // this cue has children so add them as well - cue.cues.forEach(childCue => { + cue.cues.forEach((childCue) => { addCueToWorkspace(workspace, childCue); }); } } -function getValuesForKeys(device, workspaceID, cue){ - device.send(`/workspace/${workspaceID}/cue_id/${cue.uniqueID}/valuesForKeys`, [ - {type: 's', value: valuesForKeysString} - ]); - if(cue.cues){ - cue.cues.forEach(childCue => { +function getValuesForKeys(device, workspaceID, cue) { + device.send( + `/workspace/${workspaceID}/cue_id/${cue.uniqueID}/valuesForKeys`, + [{ type: 's', value: valuesForKeysString }] + ); + if (cue.cues) { + cue.cues.forEach((childCue) => { getValuesForKeys(device, workspaceID, childCue); }); } } -function match(testArray, patternArray){ +function match(testArray, patternArray) { let out = true; - if(testArray.length !== patternArray.length){ + if (testArray.length !== patternArray.length) { return false; } - patternArray.forEach((patternPart, i)=> { - if(testArray[i] !== patternPart && patternPart !== "*"){ + patternArray.forEach((patternPart, i) => { + if (testArray[i] !== patternPart && patternPart !== '*') { out = false; } }); return out; } - exports.heartbeat = function heartbeat(device) { heartbeatCount++; - if(Date.now() - lastElapsedUpdate > 300){ + if (Date.now() - lastElapsedUpdate > 300) { interval = 24; - }else{ + } else { interval = 1; } - if(heartbeatCount % interval === 0){ + if (heartbeatCount % interval === 0) { device.send(`/cue_id/active/preWaitElapsed`); device.send(`/cue_id/active/actionElapsed`); device.send(`/cue_id/active/postWaitElapsed`); } -}; \ No newline at end of file +}; diff --git a/plugins/qlab/styles.css b/plugins/qlab/styles.css index fe1894d..65d297e 100644 --- a/plugins/qlab/styles.css +++ b/plugins/qlab/styles.css @@ -155,7 +155,7 @@ tr.playback-position .q-gray-text { border-color: #925fc0; } .gMode-6 { - border-color: #D05B15; + border-color: #d05b15; } .gMode-, .gMode-0 { diff --git a/plugins/sacn/info.html b/plugins/sacn/info.html index 4e4c755..8aa8018 100644 --- a/plugins/sacn/info.html +++ b/plugins/sacn/info.html @@ -1 +1 @@ -

    sACN requires no configuration.

    \ No newline at end of file +

    sACN requires no configuration.

    diff --git a/plugins/sacn/main.js b/plugins/sacn/main.js index dfa31b3..ee860aa 100644 --- a/plugins/sacn/main.js +++ b/plugins/sacn/main.js @@ -1,44 +1,40 @@ const _ = require('lodash'); exports.config = { - defaultName: "sACN", - connectionType: "multicast", - defaultPort: 5568, + defaultName: 'sACN', + connectionType: 'multicast', heartbeatInterval: 5000, defaultPort: 5568, mayChangePort: false, searchOptions: { - type: "multicast", + type: 'multicast', address: getMulticastGroup(1), port: 5568, - validateResponse (msg, info) { - return msg.toString('utf8', 4, 13) === "ASC-E1.17"; - } - } -} + validateResponse(msg, info) { + return msg.toString('utf8', 4, 13) === 'ASC-E1.17'; + }, + }, +}; exports.ready = function ready(device) { - const d = device; d.data.universes = {}; - d.data.source = "Unknown Source"; + d.data.source = 'Unknown Source'; d.data.orderedUniverses = []; // device.draw(); - for(let i = 1; i <= 16; i++){ + for (let i = 1; i <= 16; i++) { d.connection.addMembership(getMulticastGroup(i)); } - }; exports.data = function data(_device, buf) { - const universeIndex = buf.readUInt16BE(113); const device = _device; let universe = device.data.universes[universeIndex]; - if(!universe){ + if (!universe) { device.data.universes[universeIndex] = {}; universe = device.data.universes[universeIndex]; } @@ -48,7 +44,7 @@ exports.data = function data(_device, buf) { universe.cid = buf.toString('hex', 22, 38); universe.slots = buf.slice(126); - if(buf.readUInt8(125) !== 0){ + if (buf.readUInt8(125) !== 0) { universe.startCode = buf.readUInt8(125); } @@ -56,74 +52,70 @@ exports.data = function data(_device, buf) { device.displayName = `${device.data.source} sACN`; device.data.ip = device.addresses[0]; - if(!_.includes(device.data.orderedUniverses, universeIndex)){ + if (!_.includes(device.data.orderedUniverses, universeIndex)) { device.data.orderedUniverses.push(universeIndex); device.data.orderedUniverses.sort(); universe.slotElems = []; universe.slotElemsSet = false; device.draw(); - device.update("elementCache") + device.update('elementCache'); } - device.update("universeData", { + device.update('universeData', { universeIndex, universe, - startCode: universe.startCode + startCode: universe.startCode, }); - }; -exports.heartbeat = function heartbeat(device) { - -}; +exports.heartbeat = function heartbeat(device) {}; let lastUpdate = Date.now(); -exports.update = function update(_device, doc, updateType, updateData){ - +exports.update = function update(_device, doc, updateType, updateData) { const device = _device; const data = updateData; - if(updateType === "universeData" && data.universe){ - - if(Date.now() - lastUpdate > 1000){ + if (updateType === 'universeData' && data.universe) { + if (Date.now() - lastUpdate > 1000) { lastUpdate = Date.now(); - device.update("elementCache") + device.update('elementCache'); } const $elem = doc.getElementById(`universe-${data.universeIndex}`); - if($elem && data.universe.slotElemsSet){ - - if(data.universe.priority > 0){ - for(let i = 0; i < 512; i++){ + if ($elem && data.universe.slotElemsSet) { + if (data.universe.priority > 0) { + for (let i = 0; i < 512; i++) { data.universe.slotElems[i].innerText = data.universe.slots[i]; } const $code = doc.getElementById(`universe-${data.universeIndex}-code`); - if(data.startCode === 0xDD){ - $code.innerText = "Net3" - }else if(data.startCode === 0x17){ - $code.innerText = "Text" - }else if(data.startCode === 0xCF){ - $code.innerText = "SIP" - }else if(data.startCode === 0xCC){ - $code.innerText = "RDM" + if (data.startCode === 0xdd) { + $code.innerText = 'Net3'; + } else if (data.startCode === 0x17) { + $code.innerText = 'Text'; + } else if (data.startCode === 0xcf) { + $code.innerText = 'SIP'; + } else if (data.startCode === 0xcc) { + $code.innerText = 'RDM'; } } - }else{ + } else { device.draw(); - device.update("elementCache") + device.update('elementCache'); } - }else if(updateType === "elementCache"){ - device.data.orderedUniverses.forEach(universeIndex => { - for(let i = 0; i < 512; i++){ - device.data.universes[universeIndex].slotElems[i] = doc.getElementById(`${universeIndex}-${i}`); + } else if (updateType === 'elementCache') { + device.data.orderedUniverses.forEach((universeIndex) => { + for (let i = 0; i < 512; i++) { + device.data.universes[universeIndex].slotElems[i] = doc.getElementById( + `${universeIndex}-${i}` + ); } device.data.universes[universeIndex].slotElemsSet = true; }); } -} +}; // From https://github.com/hhromic/e131-node/blob/master/lib/e131.js function getMulticastGroup(universe) { @@ -131,4 +123,4 @@ function getMulticastGroup(universe) { throw new RangeError('universe should be in the range [1-63999]'); } return `239.255.${universe >> 8}.${universe & 0xff}`; -} \ No newline at end of file +} diff --git a/plugins/watchout/main.js b/plugins/watchout/main.js index 896784d..683c680 100644 --- a/plugins/watchout/main.js +++ b/plugins/watchout/main.js @@ -1,6 +1,6 @@ exports.config = { - defaultName: "Dataton Watchout", - connectionType: "TCPsocket", + defaultName: 'Dataton Watchout', + connectionType: 'TCPsocket', heartbeatInterval: 500, defaultPort: 3040, mayChangePort: false, @@ -8,11 +8,11 @@ exports.config = { type: 'TCPport', searchBuffer: Buffer.from('authenticate 1\n', 'ascii'), testPort: 3040, - validateResponse (msg, info) { + validateResponse(msg, info) { return msg.toString().substring(0, 5) === 'Ready'; - } - } -} + }, + }, +}; exports.ready = function ready(device) { device.send('authenticate 1\n'); @@ -24,12 +24,11 @@ exports.data = function data(_device, _message) { if (message.substring(0, 5) === 'Ready') { device.send('getStatus\n'); - - }else if (message.substring(0, 5) === 'Reply') { + } else if (message.substring(0, 5) === 'Reply') { const arr = message.split(' '); device.data.showName = ''; - + let i = 0; while (arr[i][arr[i].length - 1] !== '"') { i++; diff --git a/plugins/x32/main.js b/plugins/x32/main.js index 700a961..a7d1953 100644 --- a/plugins/x32/main.js +++ b/plugins/x32/main.js @@ -1,6 +1,6 @@ exports.config = { - defaultName: "X32 Mixer", - connectionType: "osc-udp", + defaultName: 'X32 Mixer', + connectionType: 'osc-udp', heartbeatInterval: 9000, defaultPort: 10023, mayChangePort: false, @@ -11,9 +11,9 @@ exports.config = { listenPort: 0, validateResponse(msg, info) { return msg.toString().indexOf('/xinfo') === 0; - } - } -} + }, + }, +}; exports.ready = function ready(device) { const d = device; diff --git a/plugins/xair/main.js b/plugins/xair/main.js index 25e70d4..cac150b 100644 --- a/plugins/xair/main.js +++ b/plugins/xair/main.js @@ -1,6 +1,6 @@ exports.config = { - defaultName: "X Air Mixer", - connectionType: "UDPsocket", + defaultName: 'X Air Mixer', + connectionType: 'UDPsocket', heartbeatInterval: 10000, defaultPort: 10024, mayChangePort: false, @@ -11,9 +11,9 @@ exports.config = { listenPort: 0, validateResponse(msg, info) { return msg.toString().indexOf('/xinfo') === 0; - } - } -} + }, + }, +}; exports.ready = function ready(device) { const d = device; diff --git a/plugins/xair/styles.css b/plugins/xair/styles.css index 06544f5..dd7424d 100644 --- a/plugins/xair/styles.css +++ b/plugins/xair/styles.css @@ -128,7 +128,7 @@ input[type='range']::-webkit-slider-runnable-track { border-radius: 4px; } -.infin{ +.infin { font-size: 20px; vertical-align: middle; } diff --git a/preload.js b/preload.js index ef6d29c..e3bd5a2 100644 --- a/preload.js +++ b/preload.js @@ -16,13 +16,13 @@ window.init = function init() { ipcRenderer.send('enableSearchAll'); // load autoUpdate setting from storage and send to main process - const autoUpdate = JSON.parse(localStorage.getItem('autoUpdate')) - if(autoUpdate !== undefined && autoUpdate !== null){ - if(autoUpdate){ + const autoUpdate = JSON.parse(localStorage.getItem('autoUpdate')); + if (autoUpdate !== undefined && autoUpdate !== null) { + if (autoUpdate) { ipcRenderer.send('checkForUpdates'); } // send message so main process knows the state of autoUpdate - ipcRenderer.send('setAutoUpdate', autoUpdate) + ipcRenderer.send('setAutoUpdate', autoUpdate); } PLUGINS.init(() => { @@ -35,31 +35,39 @@ window.init = function init() { SEARCH.searchAll(); }; - document.getElementById('device-settings-table').onclick = function settingsClick(e) { - e.stopPropagation(); - }; - - document.getElementById('device-settings-name').onchange = function nameChange(e) { - e.stopPropagation(); - DEVICE.changeActiveName(e.target.value); - }; - - document.getElementById('device-settings-plugin-dropdown').onchange = function dropdownChange(e) { - e.stopPropagation(); - DEVICE.changeActiveType(e.target.value); - }; + document.getElementById('device-settings-table').onclick = + function settingsClick(e) { + e.stopPropagation(); + }; - document.getElementById('device-settings-ip').onchange = function ipChange(e) { + document.getElementById('device-settings-name').onchange = + function nameChange(e) { + e.stopPropagation(); + DEVICE.changeActiveName(e.target.value); + }; + + document.getElementById('device-settings-plugin-dropdown').onchange = + function dropdownChange(e) { + e.stopPropagation(); + DEVICE.changeActiveType(e.target.value); + }; + + document.getElementById('device-settings-ip').onchange = function ipChange( + e + ) { e.stopPropagation(); DEVICE.changeActiveIP(e.target.value); }; - document.getElementById('device-settings-port').onchange = function portChange(e) { - e.stopPropagation(); - DEVICE.changeActivePort(e.target.value); - }; + document.getElementById('device-settings-port').onchange = + function portChange(e) { + e.stopPropagation(); + DEVICE.changeActivePort(e.target.value); + }; - document.getElementById('device-settings-pin').onchange = function pinChange(e) { + document.getElementById('device-settings-pin').onchange = function pinChange( + e + ) { e.stopPropagation(); if (e.target.checked) { VIEW.pinActiveDevice(); @@ -68,7 +76,6 @@ window.init = function init() { } }; - document.getElementById('save-slot-1').onclick = function slot1click(e) { e.stopPropagation(); SAVESLOTS.loadSlot(1); @@ -84,10 +91,11 @@ window.init = function init() { SAVESLOTS.loadSlot(3); }; - document.getElementById('refresh-device-button').onclick = function refreshClick(e) { - e.stopPropagation(); - DEVICE.refreshActive(); - }; + document.getElementById('refresh-device-button').onclick = + function refreshClick(e) { + e.stopPropagation(); + DEVICE.refreshActive(); + }; document.getElementById('device-list').onclick = function listClick(e) { e.stopPropagation(); @@ -97,26 +105,25 @@ window.init = function init() { } }; - document.getElementById('add-device-button').onchange = function addDeviceClick(e) { - DEVICE.registerDevice({ - type: e.target.value, - defaultName: 'New Device', - port: undefined, - addresses: [], - }); - e.target.selectedIndex = 0; + document.getElementById('add-device-button').onchange = + function addDeviceClick(e) { + DEVICE.registerDevice({ + type: e.target.value, + defaultName: 'New Device', + port: undefined, + addresses: [], + }); + e.target.selectedIndex = 0; - SAVESLOTS.saveAll(); - }; + SAVESLOTS.saveAll(); + }; document.onkeyup = function keyUp(e) { - if(e.key === 'ArrowUp'){ + if (e.key === 'ArrowUp') { VIEW.selectPreviousDevice(); - - }else if(e.key === 'ArrowDown'){ + } else if (e.key === 'ArrowDown') { VIEW.selectNextDevice(); - - }else if(e.key === 'Tab'){ + } else if (e.key === 'Tab') { if ( document.activeElement.tagName !== 'INPUT' && document.activeElement.tagName !== 'SELECT' @@ -124,10 +131,11 @@ window.init = function init() { document.getElementById('device-settings-name').select(); } } - }; - document.getElementById('device-list-col').onclick = function deviceListClick(e) { + document.getElementById('device-list-col').onclick = function deviceListClick( + e + ) { try { document .querySelector('#device-list .active-device') @@ -170,22 +178,20 @@ ipcRenderer.on('resetViews', (event, message) => { }); ipcRenderer.on('loadSlot', (event, slot) => { - if(slot){ + if (slot) { SAVESLOTS.loadSlot(slot); } }); - // message from main process to set autoUpdate state -ipcRenderer.on('setAutoUpdate',(event,autoUpdate)=>{ - localStorage.setItem('autoUpdate',autoUpdate); - if(autoUpdate){ +ipcRenderer.on('setAutoUpdate', (event, autoUpdate) => { + localStorage.setItem('autoUpdate', autoUpdate); + if (autoUpdate) { ipcRenderer.send('checkForUpdates'); } // message to main process that we have updated the state - ipcRenderer.send('setAutoUpdate',autoUpdate); -}) - + ipcRenderer.send('setAutoUpdate', autoUpdate); +}); function switchClass(element, className) { try { @@ -195,4 +201,4 @@ function switchClass(element, className) { } element.classList.add(className); } -window.switchClass = switchClass; \ No newline at end of file +window.switchClass = switchClass; diff --git a/src/device.js b/src/device.js index 9dc9a99..caffbbc 100644 --- a/src/device.js +++ b/src/device.js @@ -69,7 +69,7 @@ function registerDevice(newDevice) { devices[id].plugin = PLUGINS.all[newDevice.type]; if ( - Object.keys(devices[id].fields).length == 0 && + Object.keys(devices[id].fields).length === 0 && PLUGINS.all[newDevice.type].config.fields ) { PLUGINS.all[newDevice.type].config.fields.forEach((field) => { @@ -206,7 +206,7 @@ function initDeviceConnection(id) { device.send = (data) => {}; } - //device.plugin = plugins[type]; + // device.plugin = plugins[type]; return true; } diff --git a/src/index.css b/src/index.css index 7d9717d..13a5f26 100644 --- a/src/index.css +++ b/src/index.css @@ -118,13 +118,13 @@ a { width: 210px; pointer-events: none; } -#device-inspector{ +#device-inspector { position: absolute; bottom: 0px; width: 100%; } #device-tools { - /* position: absolute; + /* position: absolute; bottom: 210px; left: 0px;*/ width: 100%; @@ -265,7 +265,7 @@ input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } -input:disabled{ +input:disabled { color: gray; cursor: not-allowed; } @@ -341,6 +341,6 @@ select.button:focus { } /* only allow select on input fields */ -body :not(input):not(select):not(textarea) { +body :not(input):not(select):not(textarea) { user-select: none; } diff --git a/src/index.js b/src/index.js index bfeb854..fcdfc6d 100644 --- a/src/index.js +++ b/src/index.js @@ -1 +1 @@ -window.init(); \ No newline at end of file +window.init(); diff --git a/src/plugins.js b/src/plugins.js index 24e2305..8cb4dd3 100644 --- a/src/plugins.js +++ b/src/plugins.js @@ -10,20 +10,20 @@ const allPlugins = {}; module.exports.all = allPlugins; module.exports.init = function init(callback) { - - let pluginDirectoryPath = path.normalize(path.join(__dirname, `../plugins`)); + const pluginDirectoryPath = path.normalize(path.join(__dirname, `../plugins`)); console.log(`Loading plugin files... ${pluginDirectoryPath}`); fs.readdir(pluginDirectoryPath, (err, files) => { - - files.forEach((plugin)=>{ + files.forEach((plugin) => { if (plugin[0] !== '.') { - console.log(`${plugin} started`); // eslint-disable-next-line import/no-dynamic-require - allPlugins[plugin] = require(path.join(pluginDirectoryPath, `/${plugin}/main.js`)); + allPlugins[plugin] = require(path.join( + pluginDirectoryPath, + `/${plugin}/main.js` + )); const p = allPlugins[plugin]; @@ -35,50 +35,46 @@ module.exports.init = function init(callback) { }; p.template = _.template( - fs.readFileSync(path.join(pluginDirectoryPath, `/${plugin}/template.ejs`), + fs.readFileSync( + path.join(pluginDirectoryPath, `/${plugin}/template.ejs`), 'utf8' ) ); p.info = _.template( - fs.readFileSync(path.join(pluginDirectoryPath, `/${plugin}/info.html`), + fs.readFileSync( + path.join(pluginDirectoryPath, `/${plugin}/info.html`), 'utf8' ) ); + // if (p.heartbeatTimeout === undefined || p.heartbeatTimeout < 50) { + if (p.config.heartbeatTimeout) { + p.heartbeatTimeout = p.config.heartbeatInterval * 1.5; + } else { + p.heartbeatTimeout = 10000; + } + // } - //if (p.heartbeatTimeout === undefined || p.heartbeatTimeout < 50) { - if(p.config.heartbeatTimeout){ - p.heartbeatTimeout = p.config.heartbeatInterval * 1.5; - }else{ - p.heartbeatTimeout = 10000; - } - //} - - if(p.config.heartbeatInterval){ - p.heartbeatInterval = Math.max(50, p.config.heartbeatInterval); - }else{ - p.heartbeatInterval = 5000; - } - - // p.fields = {}; - // if(p.config.fields){ - // p.config.fields.forEach(field => { - // p.fields[field.key] = field.value; - // }); - // } - - + if (p.config.heartbeatInterval) { + p.heartbeatInterval = Math.max(50, p.config.heartbeatInterval); + } else { + p.heartbeatInterval = 5000; + } + // p.fields = {}; + // if(p.config.fields){ + // p.config.fields.forEach(field => { + // p.fields[field.key] = field.value; + // }); + // } // if (p.heartbeatInterval === undefined || p.heartbeatInterval < 50) { // p.heartbeatInterval = 5000; // } - } }); callback(); - }); }; diff --git a/src/saveSlots.js b/src/saveSlots.js index eed9462..2dc4aad 100644 --- a/src/saveSlots.js +++ b/src/saveSlots.js @@ -19,7 +19,7 @@ function loadSlot(slotIndex) { VIEW.toggleSlotButtons(slotIndex); activeSlot = slotIndex; - Object.keys(DEVICE.all).forEach((d)=>{ + Object.keys(DEVICE.all).forEach((d) => { DEVICE.changePinIndex(DEVICE.all[d], false); }); VIEW.resetPinned(); @@ -33,7 +33,6 @@ function loadSlot(slotIndex) { if (device.id === savedDevice.id) { VIEW.pinDevice(device); VIEW.switchDevice(device.id); - } else if ( device.addresses[0] === savedDevice.addresses[0] && device.type === savedDevice.type && @@ -43,12 +42,10 @@ function loadSlot(slotIndex) { VIEW.switchDevice(device.id); } }); - }); -}; +} module.exports.loadSlot = loadSlot; - module.exports.loadDevices = function loadDevices() { console.log(`Loading ${savedDevices.length} saved devices...`); @@ -60,12 +57,11 @@ module.exports.loadDevices = function loadDevices() { port: savedDevices[i].port, addresses: savedDevices[i].addresses, id: savedDevices[i].id, - fields: savedDevices[i].fields + fields: savedDevices[i].fields, }); } }; - module.exports.saveAll = function saveAll() { console.log('Saving...'); const currentPins = VIEW.getPinnedDevices(); @@ -75,7 +71,7 @@ module.exports.saveAll = function saveAll() { savedSlots[activeSlot][i] = { addresses: currentPins[i].addresses, type: currentPins[i].type, - id: currentPins[i].id + id: currentPins[i].id, }; } localStorage.setItem('savedSlots', JSON.stringify(savedSlots)); @@ -94,15 +90,13 @@ module.exports.saveAll = function saveAll() { defaultName: device.defaultName, port: device.port, id: device.id, - fields: device.fields + fields: device.fields, }; i++; }); localStorage.setItem('savedDevices', JSON.stringify(savedDevices)); - }; - module.exports.deleteFromSlots = function deleteFromSlots(device) { for (let i = 1; i <= 3; i++) { console.log(savedSlots); @@ -114,12 +108,10 @@ module.exports.deleteFromSlots = function deleteFromSlots(device) { } }; - module.exports.reloadActiveSlot = function reloadActiveSlot() { loadSlot(activeSlot); }; - module.exports.resetSlots = function resetSlots() { localStorage.clear(); }; diff --git a/src/search.js b/src/search.js index 8f23d84..526ac16 100644 --- a/src/search.js +++ b/src/search.js @@ -15,13 +15,17 @@ let allServers = false; function getServers() { const interfaces = os.networkInterfaces(); const result = []; - + Object.keys(interfaces).forEach((key) => { const addresses = interfaces[key]; for (let i = addresses.length; i--; ) { const address = addresses[i]; - if (address.family === 'IPv4' && !address.internal && address.address.substring(0, 3)!="169") { + if ( + address.family === 'IPv4' && + !address.internal && + address.address.substring(0, 3) !== '169' + ) { const subnet = ip.subnet(address.address, address.netmask); let current = ip.toLong(subnet.firstAddress); const last = ip.toLong(subnet.lastAddress) - 1; @@ -31,7 +35,6 @@ function getServers() { } }); return result; - } const searchSockets = []; @@ -43,9 +46,9 @@ function searchAll() { ipcRenderer.send('disableSearchAll', ''); document.getElementById('search-button').style.opacity = 0.2; - // Removed this block to fix #21 + // Removed this block to fix #21 // "After adding devices via search, searching again leaves all devices stuck in reload state" - // + // // Object.keys(DEVICE.all).forEach((i) => { // DEVICE.infoUpdate(DEVICE.all[i], 'status', 'refresh'); // }); @@ -63,26 +66,21 @@ function searchAll() { Object.keys(PLUGINS.all).forEach((p) => { const plugin = PLUGINS.all[p]; - + try { const t = plugin.config.searchOptions.type; - if(t === 'TCPport'){ + if (t === 'TCPport') { if (TCPFlag) { searchTCP(p, plugin.config); } - - }else if(t === 'Bonjour'){ + } else if (t === 'Bonjour') { searchBonjour(p, plugin.config); - - }else if(t === 'UDPsocket'){ + } else if (t === 'UDPsocket') { searchUDP(p, plugin.config); - - }else if(t === 'multicast'){ + } else if (t === 'multicast') { searchMulticast(p, plugin.config); - } - } catch (err) { console.error(`Unable to search for plugin ${p}`); } @@ -102,14 +100,11 @@ function searchAll() { ipcRenderer.send('enableSearchAll', ''); }, 10000); -}; +} module.exports.searchAll = searchAll; - - function searchBonjour(pluginType, plugin) { bonjour.find({ type: plugin.searchOptions.bonjourName }, (e) => { - const validAddresses = []; e.addresses.forEach((address) => { if (address.indexOf(':') === -1) { @@ -124,20 +119,22 @@ function searchBonjour(pluginType, plugin) { addresses: validAddresses, }); }); - -}; - +} function searchTCP(pluginType, plugin) { for (let i = 0; i < allServers.length; i++) { TCPtest(allServers[i], pluginType, plugin); } -}; +} function TCPtest(ipAddr, pluginType, plugin) { - const client = net.createConnection(plugin.searchOptions.testPort, ipAddr, () => { - client.write(plugin.searchOptions.searchBuffer); - }); + const client = net.createConnection( + plugin.searchOptions.testPort, + ipAddr, + () => { + client.write(plugin.searchOptions.searchBuffer); + } + ); client.on('data', (data) => { if (plugin.searchOptions.validateResponse(data)) { DEVICE.registerDevice({ @@ -152,14 +149,12 @@ function TCPtest(ipAddr, pluginType, plugin) { client.on('error', (err) => { // no device here }); -}; - +} function searchUDP(pluginType, plugin) { const i = searchSockets.push(dgram.createSocket('udp4')) - 1; searchSockets[i].bind(plugin.searchOptions.listenPort, () => { - // searchSockets[i].send( // plugin.searchOptions.searchBuffer, // plugin.searchOptions.devicePort, @@ -205,16 +200,15 @@ function searchUDP(pluginType, plugin) { searchSockets[i].on('listening', () => { searchSockets[i].setBroadcast(true); searchSockets[i].send( - plugin.searchOptions.searchBuffer, - plugin.searchOptions.devicePort, - '255.255.255.255', - (err) => { - // console.log(err) - } - ); + plugin.searchOptions.searchBuffer, + plugin.searchOptions.devicePort, + '255.255.255.255', + (err) => { + // console.log(err) + } + ); }); -}; - +} function searchMulticast(pluginType, plugin) { const socket = dgram.createSocket('udp4'); diff --git a/src/view.js b/src/view.js index bd7c55e..8d5f499 100644 --- a/src/view.js +++ b/src/view.js @@ -12,9 +12,7 @@ module.exports.init = function init() { populatePluginLists(); }; - function drawDeviceFrame(id) { - const $deviceDrawArea = document.getElementById(`device-${id}-draw-area`); const $devicePinned = document.getElementById(`device-${id}-pinned`); @@ -30,7 +28,8 @@ function drawDeviceFrame(id) { // scrollbar styles are inline to prevent the styles flickering in str += '