// Read-only snapshot of the ATEM switcher's audio (Fairlight) and camera control (CCU) state, // printed as one JSON line. Uses the atem-connection library inside Companion's ATEM module, // run with Companion's own node. Nothing is sent that changes the switcher. // // node atem_state.js [seconds to collect CCU values] const [ip, lib, secs] = process.argv.slice(2) const { Atem } = require(lib) const ccu = {} const atem = new Atem({ disableMultithreaded: true }) atem.on('receivedCommands', (cmds) => { for (const c of cmds) { if (c.constructor && c.constructor.rawName === 'CCdP') { const p = c.properties const v = p.numberData && p.numberData.length ? p.numberData : p.boolData && p.boolData.length ? p.boolData : p.bigintData && p.bigintData.length ? p.bigintData.map(Number) : p.stringData ccu[`${c.source}/${c.category}/${c.parameter}`] = v } } }) const done = (out, code) => { process.stdout.write(JSON.stringify(out) + '\n') atem.disconnect().finally(() => process.exit(code)) setTimeout(() => process.exit(code), 2000) } setTimeout(() => done({ error: 'no connection to the switcher' }, 2), 15000) atem.on('connected', () => { setTimeout(() => { const inputs = {} const fl = (atem.state.fairlight && atem.state.fairlight.inputs) || {} for (const [id, input] of Object.entries(fl)) { if (!input) continue for (const [sid, src] of Object.entries(input.sources || {})) { if (!src || !src.properties) continue inputs[`${id}/${sid}`] = { mixOption: src.properties.mixOption, faderGain: src.properties.faderGain } } } done({ model: atem.state.info && atem.state.info.productIdentifier, fairlight: inputs, ccu }, 0) }, (Number(secs) || 3) * 1000) }) atem.connect(ip)