From 66405aefa0cd21ae48bfaef2e7ff26280b669e72 Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Sat, 21 Oct 2023 10:13:13 -0500 Subject: [PATCH 1/8] initial lightfactory support --- plugins/lightfactory/info.html | 0 plugins/lightfactory/main.js | 99 +++++++++++++++++++++++++++++++ plugins/lightfactory/styles.css | 28 +++++++++ plugins/lightfactory/template.ejs | 31 ++++++++++ 4 files changed, 158 insertions(+) create mode 100644 plugins/lightfactory/info.html create mode 100644 plugins/lightfactory/main.js create mode 100644 plugins/lightfactory/styles.css create mode 100644 plugins/lightfactory/template.ejs diff --git a/plugins/lightfactory/info.html b/plugins/lightfactory/info.html new file mode 100644 index 0000000..e69de29 diff --git a/plugins/lightfactory/main.js b/plugins/lightfactory/main.js new file mode 100644 index 0000000..edbd269 --- /dev/null +++ b/plugins/lightfactory/main.js @@ -0,0 +1,99 @@ +exports.config = { + defaultName: 'LightFactory', + connectionType: 'TCPsocket', + remotePort: 3100, + mayChangePorts: true, + searchOptions: { + type: 'TCPport', + searchBuffer: Buffer.from('authenticate 1\n', 'ascii'), + testPort: 3100, + validateResponse(msg, info) { + return msg.toString().substring(0, 5) === 'Ready'; + }, + }, +}; + +exports.ready = function ready(device) { + device.data.cueLists = {}; + device.send('get cue list\n'); +}; + +function cleanMessage(message) { + return message.split('\n').filter((line) => line.trim() !== ''); +} + +// TODO(jwetzell): process updates? are they live or do we need to poll? +exports.data = function data(_device, _message) { + const message = _message.toString(); + const device = _device; + console.log(message); + + if (message.includes('Current Cue List')) { + // NOTE(jwetzell): loading cue list info + const cueListMessage = cleanMessage(message); + if (cueListMessage.includes('') && cueListMessage.includes('')) { + console.log('message is a cue list message'); + const cueListstartIndex = cueListMessage.indexOf(''); + const cueListFinishedIndex = cueListMessage.indexOf(''); + + for (let i = cueListstartIndex + 1; i < cueListFinishedIndex; i++) { + const cueListName = cueListMessage[i]; + if (device.data.cueLists[cueListName] === undefined) { + device.data.cueLists[cueListName] = { + name: cueListName, + cues: {}, + active: false, + }; + } else { + // NOTE(jwetzell): reset active as it will be computed in a few lines + device.data.cueLists[cueListName].active = false; + } + } + const activeCueListLine = cueListMessage[cueListFinishedIndex + 1]; + if (activeCueListLine) { + const activeCueListLineParts = activeCueListLine.split('Current Cue List'); + const activeCueList = activeCueListLineParts[activeCueListLineParts.length - 1].trim(); + if (activeCueList && device.data.cueLists[activeCueList] !== undefined) { + device.data.cueLists[activeCueList].active = true; + } + } + device.data.cueListToLoad = Object.keys(device.data.cueLists)[0]; + console.log(device.data.cueListToLoad); + device.send(`get cues ${device.data.cueListToLoad}\n`); + } + } else if (message.includes('Current Live Cue')) { + // NOTE(jwetzell): loading cues for a cue list + const cueList = device.data.cueLists[device.data.cueListToLoad]; + const cueListCuesMessage = cleanMessage(message); + + console.log(cueListCuesMessage); + if (cueListCuesMessage.includes('') && cueListCuesMessage.includes('')) { + console.log('message is a cue list message'); + const cuesStartIndex = cueListCuesMessage.indexOf(''); + const cuesFinishedIndex = cueListCuesMessage.indexOf(''); + for (let i = cuesStartIndex + 1; i < cuesFinishedIndex; i++) { + const cueName = cueListCuesMessage[i]; + if (cueList.cues[cueName] === undefined) { + cueList.cues[cueName] = { + name: cueName, + active: false, + }; + } else { + // NOTE(jwetzell): reset active as it will be computed in a few lines + cueList[cueName].active = false; + } + } + } + const cueListNames = Object.keys(device.data.cueLists); + + const cueListIndex = cueListNames.indexOf(device.data.cueListToLoad); + device.data.cueListToLoad = cueListNames.at(cueListIndex + 1); + if (device.data.cueListToLoad !== undefined) { + device.send(`get cues ${device.data.cueListToLoad}\n`); + } + } + device.draw(); +}; + +// TODO(jwetzell): keep alive? refresh cue list info? +exports.heartbeat = function heartbeat(device) {}; diff --git a/plugins/lightfactory/styles.css b/plugins/lightfactory/styles.css new file mode 100644 index 0000000..0fd89a2 --- /dev/null +++ b/plugins/lightfactory/styles.css @@ -0,0 +1,28 @@ +section { + background-color: #2d2d2d; + border: gray 1px solid; + padding: 10px; + padding-top: 0px; + border-radius: 10px; + overflow: hidden; +} +table { + width: 100%; + border: #414142 1px solid; + background-color: #272727; +} +table, +th, +td { + border-collapse: collapse; + color: #ccc; + font-size: 13px; +} +th { + padding: 2px 10px; +} +td { + text-align: center; + padding: 2px 7px; + font-family: 'Courier New', Courier, monospace; +} diff --git a/plugins/lightfactory/template.ejs b/plugins/lightfactory/template.ejs new file mode 100644 index 0000000..aa6feab --- /dev/null +++ b/plugins/lightfactory/template.ejs @@ -0,0 +1,31 @@ +
+

<%= listName %>

+
+ +
+ + <% Object.entries(data.cueLists).forEach(([cueListName,cueListData]) => { %> + +
+

Cue List: <%= cueListData.name %> <%= cueListData.active ? '(Active)': '' %>

+ + + + + + + + <% Object.entries(cueListData.cues).forEach(([cueName,cueData]) => { %> + + + + + + <% }); %> +
Name
<%= cueName %>
+
+ + <% + }); + %> +
\ No newline at end of file From cedf6e34aa95c4158e6fa519a201835c757efafd Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Sat, 21 Oct 2023 14:44:34 -0500 Subject: [PATCH 2/8] add lightfactory icon --- plugins/lightfactory/icon.png | Bin 0 -> 2432 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 plugins/lightfactory/icon.png diff --git a/plugins/lightfactory/icon.png b/plugins/lightfactory/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..08cba27145c13f0c547ee31f9ab731fe811d21e0 GIT binary patch literal 2432 zcmY*bc|4R|8-6Cdn23lOglSMoW>91>V_$1**$HEbVaCimGs0_W8!>)*CA%h}1=;d_ zb|R5T2wAg@lC8x$N|tX_pZELD^Sht>I`_Hm>pu7Soj(r4+R{`IEC~hxK+xRG$d=<} zI3~;Y2j{;?h~(us5Y^Vy04V7?Jja>9yv=N}0PxpQ0JuU2fDKOQ$}9i`s{p{fCje-r z0|11~thLeMEYN+O%r9cGzzGiK19(6p02c>=I3EC#1bBX80I&c_?O|Kc@!y<%01$%* zxPNn8IDU7Sag3Aw+jC`u{!z@{x5t*r=Gw!uyJ_4~)Jl%yC!0A_0bsxAu7Q9|CX}P* zjJI>5IbqLfd6S3;oDa#%7ZFM%?`8oyp;{bB^rhiop+rIuRVx$?|H;te;N3714*N-= z1)||jSZkOeiQ)^>K&T*8;21Cr2GgPVT-35PGWng(nW5qSG#Xh8i3|w|L4=$_kSKmg zRTK(^R8d2!sVQ?9%G9tR8ZJ~hh${C>D!qJ7CQ1+h_v@cWmXp9&m)aMv1> z4mHIbd(o=9#G_rAFr+zznW11Ny1N$6gjf0f2#C3*);nGwwz54@pAiy2d_DHvr0vSr zy625LD9M^p_KVe3)_9p=c3*1lmTW}UX1rST2WCK>sYNI=s)I2AoKj-oc_bm;kK)tF zHUV}ix@6a!zT08#K1D}P4>&yz#FZLWi#{^H#y~|f<>|FCQ*-Q@hPwIanwtU-4pB|$ z9+tt1aW}wSPbJ zdyigdmqJGX>{Wt|#!|%7xe4L&vfT02+F`XN=ilXnfinDar zZZ>S*Ylq%#%*<_MUuJomWS>?t?#Yjsx%{f^b^5Uhlg@jmQ1=qrV-jLC{-w7w5V5L0 z=#lewNBxpf7~K1PErV$S%T{uK6@^)Z{?RoG_s)rVQdDakUFdtzw$&jy zg>hj?b1fxp0qJ1ZqMUm^Rg*3N!2%9IVSIwO+TRUq+KgWu{j&pVH@HhB+4V^n`tbe!#hL4Eoq64j80E>{G#-x3S(p6(0te4+Tp`++Wbbu)`_$z{*>9%3Q73_-IIjJYj0B3V^RbJ z?DYq%$&xTB<_Q z9Ey~>Ml>>gitwIA>*A$>;`NfRBprbjftK@m&XNAGz*iLu(A3VF%;`3)W|_pd^o0a; znmb;$QEg01JOlGa0+W8E0Xt z@f8hMLbg9fR7+Nb?v&kHsPI&AywBb~^-OAPEZ>oj>+T8Z@b0`IP5T9lGF420 z7_D|Zn@Jpx^ox<{NCT4~$(GyO!KIM!jUioak{|Me%qFwQ7*DMm1nI~?X@w)jMCqmD z6uI-)Gq2W`FQ=v8`cKX~^?zjzI9LHIB2$e~hXh95tXNal#R!JvabhLw zv$G~HPks5QH)$Cz_>jHef1k3lnXmXj%;xocz^TR2;(hh*#82P9<=WqaW6Jp}w@L^W zqYoF&E2kiLx^K#8Qa3}z#%FoS@qP(1=-T?4nlNW9wv+IFuFqdjB5-@1VY<1U^iJ&4 z`VYtVsPYkKsACb?n()-R9-&D)AjH<^8@Zm#$5I@+t-(pKrq;}wM)-gNe%frFZpPuH_DXAsvT;toz3uRUrmxcq9%{XM8dtpB2?pf^pM~bK$gDUBOn>) zD`No>q`oY!6YWC_nQU~g)>$}cn+5$LdbkGx`>NZrLl;k?#vptYv`X-cK{G9PCX=Ol z{DTOD)qn#tcg|&68L~&8DMpEbm5$)Wx23*DH0GXd-`Xi@L4C(!uNJ#OT)&Kcf8V$T Y3D&2SMU1@&Et; literal 0 HcmV?d00001 From 7208154a68c78e24b4122946f311badfdb551d53 Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Sat, 21 Oct 2023 14:45:13 -0500 Subject: [PATCH 3/8] add lightfactory discovery --- plugins/lightfactory/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/lightfactory/main.js b/plugins/lightfactory/main.js index edbd269..2bcb3a9 100644 --- a/plugins/lightfactory/main.js +++ b/plugins/lightfactory/main.js @@ -5,10 +5,10 @@ exports.config = { mayChangePorts: true, searchOptions: { type: 'TCPport', - searchBuffer: Buffer.from('authenticate 1\n', 'ascii'), + searchBuffer: Buffer.from('\n', 'ascii'), testPort: 3100, validateResponse(msg, info) { - return msg.toString().substring(0, 5) === 'Ready'; + return msg.toString().includes('LightFactory'); }, }, }; From 5600c64f6977e637336e0d78c5522d9e5cd4ad63 Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Sat, 21 Oct 2023 14:45:29 -0500 Subject: [PATCH 4/8] parse cue updates from lightfactory --- plugins/lightfactory/main.js | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/plugins/lightfactory/main.js b/plugins/lightfactory/main.js index 2bcb3a9..08ce3ce 100644 --- a/plugins/lightfactory/main.js +++ b/plugins/lightfactory/main.js @@ -22,17 +22,15 @@ function cleanMessage(message) { return message.split('\n').filter((line) => line.trim() !== ''); } -// TODO(jwetzell): process updates? are they live or do we need to poll? +// TODO(jwetzell): try to grab notes? exports.data = function data(_device, _message) { const message = _message.toString(); const device = _device; - console.log(message); if (message.includes('Current Cue List')) { // NOTE(jwetzell): loading cue list info const cueListMessage = cleanMessage(message); if (cueListMessage.includes('') && cueListMessage.includes('')) { - console.log('message is a cue list message'); const cueListstartIndex = cueListMessage.indexOf(''); const cueListFinishedIndex = cueListMessage.indexOf(''); @@ -58,7 +56,6 @@ exports.data = function data(_device, _message) { } } device.data.cueListToLoad = Object.keys(device.data.cueLists)[0]; - console.log(device.data.cueListToLoad); device.send(`get cues ${device.data.cueListToLoad}\n`); } } else if (message.includes('Current Live Cue')) { @@ -66,9 +63,7 @@ exports.data = function data(_device, _message) { const cueList = device.data.cueLists[device.data.cueListToLoad]; const cueListCuesMessage = cleanMessage(message); - console.log(cueListCuesMessage); if (cueListCuesMessage.includes('') && cueListCuesMessage.includes('')) { - console.log('message is a cue list message'); const cuesStartIndex = cueListCuesMessage.indexOf(''); const cuesFinishedIndex = cueListCuesMessage.indexOf(''); for (let i = cuesStartIndex + 1; i < cuesFinishedIndex; i++) { @@ -83,14 +78,38 @@ exports.data = function data(_device, _message) { cueList[cueName].active = false; } } - } - const cueListNames = Object.keys(device.data.cueLists); + const activeCueLine = cueListCuesMessage[cuesFinishedIndex + 1]; + if (activeCueLine) { + const activeCueLineParts = activeCueLine.split('Current Live Cue'); + const activeCue = activeCueLineParts[activeCueLineParts.length - 1].trim(); + if (activeCue && cueList.cues[activeCue] !== undefined) { + cueList.cues[activeCue].active = true; + } + } + } + // NOTE(jwetzell): load next cue list's cues if there is one + const cueListNames = Object.keys(device.data.cueLists); const cueListIndex = cueListNames.indexOf(device.data.cueListToLoad); device.data.cueListToLoad = cueListNames.at(cueListIndex + 1); if (device.data.cueListToLoad !== undefined) { device.send(`get cues ${device.data.cueListToLoad}\n`); } + } else if (message.includes('Running cue') || message.includes('Goto cue')) { + const cueUpdateMessage = message + .trim() + .slice(0, -1) + .split(':') + .map((item) => item.trim()); + if (cueUpdateMessage.length === 3) { + const cueListName = cueUpdateMessage[1]; + const updatedCueName = cueUpdateMessage[2]; + if (device.data.cueLists[cueListName] !== undefined) { + Object.entries(device.data.cueLists[cueListName].cues).forEach(([cueName, cueData]) => { + cueData.active = cueName === updatedCueName; + }); + } + } } device.draw(); }; From c5a60622d01a774c6fb351deac0c2b10c6cd3325 Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Sat, 21 Oct 2023 14:45:41 -0500 Subject: [PATCH 5/8] update template to show active cue --- plugins/lightfactory/styles.css | 7 +++++-- plugins/lightfactory/template.ejs | 6 ++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/plugins/lightfactory/styles.css b/plugins/lightfactory/styles.css index 0fd89a2..a99942e 100644 --- a/plugins/lightfactory/styles.css +++ b/plugins/lightfactory/styles.css @@ -7,7 +7,6 @@ section { overflow: hidden; } table { - width: 100%; border: #414142 1px solid; background-color: #272727; } @@ -22,7 +21,11 @@ th { padding: 2px 10px; } td { - text-align: center; + text-align: left; padding: 2px 7px; font-family: 'Courier New', Courier, monospace; } + +.active_indicator { + color: green; +} diff --git a/plugins/lightfactory/template.ejs b/plugins/lightfactory/template.ejs index aa6feab..103afd9 100644 --- a/plugins/lightfactory/template.ejs +++ b/plugins/lightfactory/template.ejs @@ -11,14 +11,16 @@ - + + <% Object.entries(cueListData.cues).forEach(([cueName,cueData]) => { %> - + + <% }); %> From 694db535fe7a9ad5f1ee47058d80dc00a4e922c5 Mon Sep 17 00:00:00 2001 From: sparks-alec Date: Mon, 28 Oct 2024 21:07:34 -0400 Subject: [PATCH 6/8] update styles --- plugins/lightfactory/main.js | 7 +++++-- plugins/lightfactory/styles.css | 35 ++++++++++++++++++++++++------- plugins/lightfactory/template.ejs | 10 +++++---- 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/plugins/lightfactory/main.js b/plugins/lightfactory/main.js index 08ce3ce..73e3a9e 100644 --- a/plugins/lightfactory/main.js +++ b/plugins/lightfactory/main.js @@ -13,9 +13,11 @@ exports.config = { }, }; -exports.ready = function ready(device) { +exports.ready = function ready(_device) { + const device = _device; device.data.cueLists = {}; device.send('get cue list\n'); + console.log('sent'); }; function cleanMessage(message) { @@ -70,7 +72,8 @@ exports.data = function data(_device, _message) { const cueName = cueListCuesMessage[i]; if (cueList.cues[cueName] === undefined) { cueList.cues[cueName] = { - name: cueName, + name: cueName.substring(cueName.indexOf(' ')), + number: cueName.substring(0, cueName.indexOf(' ')), active: false, }; } else { diff --git a/plugins/lightfactory/styles.css b/plugins/lightfactory/styles.css index a99942e..4f08140 100644 --- a/plugins/lightfactory/styles.css +++ b/plugins/lightfactory/styles.css @@ -1,31 +1,52 @@ section { - background-color: #2d2d2d; + background-color: #363636; border: gray 1px solid; - padding: 10px; + padding: 2px; padding-top: 0px; - border-radius: 10px; overflow: hidden; + font-size: 8px; + margin-bottom: 10px; + width: 300px; } table { border: #414142 1px solid; background-color: #272727; + width: 100%; } table, th, td { border-collapse: collapse; color: #ccc; - font-size: 13px; + font-size: 12px; } th { padding: 2px 10px; + border: #282828 1px solid; + background: linear-gradient(#404040, #363636); + text-align: left; } td { text-align: left; - padding: 2px 7px; - font-family: 'Courier New', Courier, monospace; + padding: 7px 7px; + font-family: sans-serif; + position: relative; +} +tr { + border-bottom: #363636 1px solid; +} +tr:nth-child(odd) { + background-color: #2a2a2a; +} +tr:nth-child(even) { + background-color: #202020; } .active_indicator { - color: green; + color: #00ff00; + font-size: 13px; + position: absolute; + top: 4px; + left: -4px; + transform: rotate(-45deg); } diff --git a/plugins/lightfactory/template.ejs b/plugins/lightfactory/template.ejs index 103afd9..2fe80ad 100644 --- a/plugins/lightfactory/template.ejs +++ b/plugins/lightfactory/template.ejs @@ -11,16 +11,18 @@
NameName
<%= cueName %><%= cueData.active ? '>': '' %><%= cueName %>
- - + + + <% Object.entries(cueListData.cues).forEach(([cueName,cueData]) => { %> - - + + + <% }); %> From e0b2db1a11d501a4bd62d381a1ef40978f2a105d Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Tue, 29 Oct 2024 13:25:40 -0500 Subject: [PATCH 7/8] add basic refresh heartbeat --- plugins/lightfactory/main.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/lightfactory/main.js b/plugins/lightfactory/main.js index 73e3a9e..7f16a81 100644 --- a/plugins/lightfactory/main.js +++ b/plugins/lightfactory/main.js @@ -3,6 +3,8 @@ exports.config = { connectionType: 'TCPsocket', remotePort: 3100, mayChangePorts: true, + heartbeatInterval: 5000, + heartbeatTimeout: 10000, searchOptions: { type: 'TCPport', searchBuffer: Buffer.from('\n', 'ascii'), @@ -118,4 +120,6 @@ exports.data = function data(_device, _message) { }; // TODO(jwetzell): keep alive? refresh cue list info? -exports.heartbeat = function heartbeat(device) {}; +exports.heartbeat = function heartbeat(device) { + device.send('get cue list\n'); +}; From 3626bda89c7cb4d261fbd128dd75b5de473ace06 Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Tue, 29 Oct 2024 13:26:15 -0500 Subject: [PATCH 8/8] rework cue list population to not leave straggling cues and populate active cue correctly --- plugins/lightfactory/main.js | 39 +++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/plugins/lightfactory/main.js b/plugins/lightfactory/main.js index 7f16a81..9cdfbf1 100644 --- a/plugins/lightfactory/main.js +++ b/plugins/lightfactory/main.js @@ -19,7 +19,6 @@ exports.ready = function ready(_device) { const device = _device; device.data.cueLists = {}; device.send('get cue list\n'); - console.log('sent'); }; function cleanMessage(message) { @@ -67,23 +66,31 @@ exports.data = function data(_device, _message) { const cueList = device.data.cueLists[device.data.cueListToLoad]; const cueListCuesMessage = cleanMessage(message); + // NOTE(jwetzell): this is so gross if (cueListCuesMessage.includes('') && cueListCuesMessage.includes('')) { const cuesStartIndex = cueListCuesMessage.indexOf(''); const cuesFinishedIndex = cueListCuesMessage.indexOf(''); + + const validCueNumbers = []; for (let i = cuesStartIndex + 1; i < cuesFinishedIndex; i++) { - const cueName = cueListCuesMessage[i]; - if (cueList.cues[cueName] === undefined) { - cueList.cues[cueName] = { - name: cueName.substring(cueName.indexOf(' ')), - number: cueName.substring(0, cueName.indexOf(' ')), - active: false, - }; - } else { - // NOTE(jwetzell): reset active as it will be computed in a few lines - cueList[cueName].active = false; - } + const cueData = cueListCuesMessage[i]; + const cueSplit = cueData.indexOf(' '); + const cueNumber = cueSplit > 0 ? cueData.substring(0, cueSplit).trim() : cueData.trim(); + const cueName = cueSplit > 0 ? cueData.substring(cueSplit + 1).trim() : ''; + cueList.cues[cueNumber] = { + name: cueName, + number: cueNumber, + active: false, + }; + validCueNumbers.push(cueNumber); } + const invalidCueNumbers = Object.keys(cueList.cues).filter((cueNumber) => !validCueNumbers.includes(cueNumber)); + + invalidCueNumbers.forEach((cueNumber) => { + delete cueList.cues[cueNumber]; + }); + const activeCueLine = cueListCuesMessage[cuesFinishedIndex + 1]; if (activeCueLine) { const activeCueLineParts = activeCueLine.split('Current Live Cue'); @@ -108,10 +115,14 @@ exports.data = function data(_device, _message) { .map((item) => item.trim()); if (cueUpdateMessage.length === 3) { const cueListName = cueUpdateMessage[1]; - const updatedCueName = cueUpdateMessage[2]; + const updatedCue = cueUpdateMessage[2]; + + const split = updatedCue.indexOf(' '); + + const updatedCueNumber = split > 0 ? updatedCue.substring(0, split).trim() : updatedCue.trim(); if (device.data.cueLists[cueListName] !== undefined) { Object.entries(device.data.cueLists[cueListName].cues).forEach(([cueName, cueData]) => { - cueData.active = cueName === updatedCueName; + cueData.active = cueData.number === updatedCueNumber; }); } }
NameCue NoDescription
<%= cueData.active ? '>': '' %><%= cueName %> <%= cueData.active ? '◢': '' %><%= cueData.number %><%= cueData.name %>