Files
Cue-View/plugins/scs/main.js
T

188 lines
6.3 KiB
JavaScript

exports.config = {
defaultName: 'Show Cue Systems',
connectionType: 'TCPsocket',
defaultPort: 58000,
mayChangePort: true,
heartbeatInterval: 5000,
searchOptions: {
type: 'TCPport',
searchBuffer: Buffer.from('/_status', 'ascii'),
testPort: 58000,
validateResponse(msg, info) {
return msg.toString().includes('/_status');
},
},
fields: [],
};
const typeMap = {
A: 'Video/Image',
E: 'Memo',
F: 'Audio File',
G: 'GoTo Cue',
H: 'Multi-File Cue',
I: 'Live Input',
J: 'Enable/Disable',
K: 'Lighting',
L: 'Level Change',
M: 'Control Send',
N: 'Note',
P: 'Playlist',
Q: 'Call Cue',
R: 'Run Program',
S: 'SFR',
T: 'Set Position',
U: 'MIDI Time Code',
};
const versionMap = {
10: 'LITE',
20: 'STD',
30: 'PRO',
40: 'PLUS',
45: 'DEMO',
50: 'PLAT',
};
exports.ready = function ready(_device) {
const device = _device;
console.log('Show Cue Systems ready');
device.data.cues = [];
device.send(Buffer.from('/_prod/gettitle'));
};
exports.data = function data(_device, message) {
const device = _device;
this.deviceInfoUpdate(device, 'status', 'ok');
const msg = message.toString();
const messagePath = msg.substring(0, msg.indexOf(' '));
if (messagePath === '/_prod/gettitle') {
const title = msg.replace(messagePath, '').replaceAll('"', '').trim();
this.deviceInfoUpdate(device, 'defaultName', title);
device.send(Buffer.from('/_info/scsversion'));
} else if (messagePath === '/_info/scsversion') {
const versionInfo = msg.replace(messagePath, '').trim().split(' ');
const versionNumber = versionInfo[0];
const versionMajor = versionNumber.substring(0, 2).replace('0', '');
const versionMinor = versionNumber.substring(2, 4).replace('0', '');
const versionPatch = versionNumber.substring(4, 6).replace('0', '');
const versionString = `${versionMajor}.${versionMinor}.${versionPatch}`;
const versionType = versionMap[versionInfo[1]];
device.data.version = {
number: versionString,
type: versionType,
};
device.send(Buffer.from('/_info/finalcue', 'ascii'));
} else if (messagePath === '/_info/finalcue') {
const cueCount = parseInt(msg.replace(messagePath, '').trim(), 10);
device.data.cues = new Array(cueCount).fill(0).map(() => ({
label: '',
page: '',
description: '',
type: { code: '', display: '' },
state: '',
activation: '',
file_info: '',
length: '',
colors: ['', ''],
}));
device.send(Buffer.from(`/_cue/getitemsn 1 QNTCLSPARZ`));
} else if (messagePath === '/_info/getcue') {
const cueLabelInfo = msg.replace(messagePath, '').trim().split(' ');
const cueNumber = parseInt(cueLabelInfo[0], 10);
const cueLabel = cueLabelInfo[1].replaceAll('"', '');
device.data.cues[cueNumber - 1].label = cueLabel;
device.draw();
} else if (messagePath === '/_cue/getpage') {
const pageInfo = msg.replace(messagePath, '').trim().match(/".*?"/g);
const cueLabel = pageInfo[0].replaceAll('"', '');
const cueIndex = device.data.cues.map((cue) => cue.label).indexOf(cueLabel);
device.data.cues[cueIndex].page = pageInfo[1].replaceAll('"', '');
if (cueIndex < device.data.cues.length - 1) {
device.send(Buffer.from(`/_cue/getpage ${device.data.cues[cueIndex + 1].label}`));
} else {
// console.log('done loading cue pages');
device.draw();
device.send(Buffer.from('/_status'));
}
} else if (messagePath === '/_cue/getname') {
const nameInfo = msg.replace(messagePath, '').trim().match(/".*?"/g);
const cueLabel = nameInfo[0].replaceAll('"', '');
const cueIndex = device.data.cues.map((cue) => cue.label).indexOf(cueLabel);
device.data.cues[cueIndex].description = nameInfo[1].replaceAll('"', '');
device.draw();
} else if (messagePath === '/_cue/gettype') {
const typeInfo = msg.replace(messagePath, '').trim().match(/".*?"/g);
const cueLabel = typeInfo[0].replaceAll('"', '');
const cueIndex = device.data.cues.map((cue) => cue.label).indexOf(cueLabel);
device.data.cues[cueIndex].type = {
code: typeInfo[1].replaceAll('"', ''),
display: typeMap[typeInfo[1].replaceAll('"', '')],
};
device.draw();
} else if (messagePath === '/_cue/getitemsn') {
const itemParts = msg
.replace(messagePath, '')
.trim('')
.match(/".*?"|\d+/g);
if (itemParts.length === 12) {
const cueIndex = itemParts[0] - 1;
device.data.cues[cueIndex].label = sanitizeSCSString(itemParts[2]);
device.data.cues[cueIndex].description = sanitizeSCSString(itemParts[3]);
device.data.cues[cueIndex].type = {
code: sanitizeSCSString(itemParts[4]),
display: typeMap[sanitizeSCSString(itemParts[4])],
};
device.data.cues[cueIndex].colors = sanitizeSCSString(itemParts[5]).split(', ');
device.data.cues[cueIndex].length = millisToString(itemParts[6]);
device.data.cues[cueIndex].state = itemParts[7];
device.data.cues[cueIndex].position = itemParts[8];
device.data.cues[cueIndex].activation = sanitizeSCSString(itemParts[9]);
device.data.cues[cueIndex].repeat = itemParts[10];
device.data.cues[cueIndex].loop = itemParts[11];
device.draw();
if (cueIndex < device.data.cues.length - 1) {
device.send(Buffer.from(`/_cue/getitemsn ${cueIndex + 2} QNTCLSPARZ`));
} else {
// console.log('done loading cue items');
device.send(Buffer.from(`/_cue/getpage ${device.data.cues[0].label}`));
}
} else {
console.error('bad response from getitems');
}
} else if (messagePath === '/_status') {
const status = parseInt(msg.split(' ')[1].trim(), 10);
console.log(`status: ${status}`);
} else {
console.error(`unhandled message path: ${messagePath}`);
}
};
function sanitizeSCSString(string) {
return string.substring(1, string.length - 1);
}
function millisToString(duration) {
const milliseconds = parseInt(duration % 1000, 10);
let seconds = parseInt((duration / 1000) % 60, 10);
const minutes = parseInt((duration / (1000 * 60)) % 60, 10);
if (minutes === 0) {
return `${seconds}.${milliseconds}`;
}
if (seconds === 0) {
return `0.${milliseconds}`;
}
if (seconds < 10) {
seconds = `0${seconds}`;
}
return `${minutes}:${seconds}.${milliseconds}`;
}
exports.heartbeat = function heartbeat(device) {};