A Node.js library for programmatically managing Q-SYS cores and Lua scripts. Deploy code to multiple systems, monitor script health, sync with files, and automate your Q-SYS infrastructure.
npm install ide_qsysimport Core from 'ide_qsys';
const { username, pin } = process.env;
const core = new Core({
ip: '192.168.1.100',
username,
pin,
verbose: false // Enable verbose logging (default: false)
});
await core.connect();
const components = await core.getComponents();
console.log(`Found ${components.length} components`);
await core.disconnect();Before you can access script components via QRC, they must be configured for script access in Q-SYS Designer.
In Q-SYS Designer, for each script component you want to access:
- Select the script component
- In the Properties panel, set "Script Access" dropdown to anything other than "None" (which is the default)
- The "Code Name" field shows the component name you'll use in your code
Important Notes:
- Script Access = "None": Component cannot be accessed via QRC
- Script Access = "All" or other options: Component can be accessed via QRC
- The "Code Name" field value is what you use as the
componentNameparameter in your API calls
// If your component's "Code Name" is "Main"
const components = await core.getComponents();
const code = await core.getCode('Main'); // Use the Code Name hereThe username and PIN are configured in Q-SYS Administrator. You need to create a user with External Control Protocol permissions:
In Q-SYS Administrator:
- Go to the user management section
- Create or edit a user (e.g., "admin")
- Set a PIN (e.g., "1234")
- Enable "External Control Protocol" permissions
- Enable "File Management Protocol" if you need file operations
const { username, pin } = process.env;
const core = new Core({
ip: '192.168.1.100', // Required: Q-SYS Core IP address
username, // Required: Username from Q-SYS Administrator
pin, // Required: PIN (also accepts 'password' or 'pw')
comp: 'Main', // Optional: Default component name
verbose: false // Optional: Enable debug logging
});Wrong credentials:
// This will throw an error
const { username } = process.env;
const core = new Core({
ip: '192.168.1.100',
username,
pin: 'wrong_pin' // Intentionally wrong PIN for demonstration
});
try {
await core.connect();
} catch (error) {
console.error(error.message); // "QRC authentication failed for 192.168.1.100: Logon required"
}Missing credentials:
// This will throw an error
const core = new Core({
ip: '192.168.1.100'
// Missing username and pin
});
try {
await core.connect();
} catch (error) {
console.error(error.message); // "QRC authentication failed for 192.168.1.100: Logon required"
}const { username, pin } = process.env;
const core = new Core({ ip: '192.168.1.100', username, pin });
await core.connect();
// Perform multiple operations efficiently
const components = await core.getComponents();
const logs = await core.collectLogs('Main');
const code = await core.getCode('Main');
await core.disconnect();const { username, pin } = process.env;
const core = new Core({ ip: '192.168.1.100', username, pin });
// These methods handle connection automatically
const components = await core.getComponentsSync();
const errors = await core.getScriptErrorsSync();Establishes a persistent connection to the Q-SYS core.
await core.connect();Closes the connection to the Q-SYS core.
const disconnected = await core.disconnect();
console.log(`Successfully disconnected: ${disconnected}`); // true if successful
// Check connection status anytime
console.log(`Currently connected: ${core.connected}`); // boolean propertyReturns: boolean - true if successfully disconnected, false otherwise
Returns an array of all components in the Q-SYS design.
// Session-based
await core.connect();
const components = await core.getComponents();
await core.disconnect();
// Single-shot
const components = await core.getComponentsSync();Returns: Array of component objects with Name and Type properties.
Gets all controls for a specific component.
// Session-based with callback
await core.connect();
await core.getControls('Main', (error, controls) => {
if (error) console.error(error);
else console.log(controls);
});
await core.disconnect();
// Single-shot
const controls = await core.getControlsSync('Main');Parameters:
componentName(string): Name of the componentcallback(function, optional): Callback function for session-based method
Returns: Array of control objects.
Gets the value of a specific control.
// Session-based
await core.connect();
const value = await core.getComponent('Main', 'Status');
await core.disconnect();
// Single-shot
const value = await core.getComponentSync('Main', 'Status');Parameters:
componentName(string): Name of the componentcontrolName(string): Name of the controlcallback(function, optional): Callback function for session-based method
Returns: Control value object.
setComponent(componentName, controlName, value, callback) / setComponentSync(componentName, controlName, value)
Sets the value of a specific control.
// Session-based
await core.connect();
await core.setComponent('Main', 'reload', 1);
await core.disconnect();
// Single-shot
await core.setComponentSync('Main', 'reload', 1);Parameters:
componentName(string): Name of the componentcontrolName(string): Name of the controlvalue(any): Value to setcallback(function, optional): Callback function for session-based method
Gets script errors for components with enhanced feedback.
// Session-based - specific component
await core.connect();
const errors = await core.getScriptErrors({ scriptName: 'Main' });
console.log(errors.Found); // true/false - component exists
console.log(errors.Message); // descriptive message
console.log(errors.Value); // error count (0 if no errors)
// Session-based - all components
const allErrors = await core.getScriptErrors();
console.log(allErrors.summary.totalScriptComponents); // total script components found
console.log(allErrors.summary.componentsWithErrors); // how many have errors
console.log(allErrors.errors); // array of components with errors
await core.disconnect();
// Single-shot
const errors = await core.getScriptErrorsSync({ scriptName: 'Main' });Parameters:
options(object, optional):scriptName(string): Specific script name to check
callback(function, optional): Callback function for session-based method
Returns:
- With
scriptName: Object withFound,Message,Value,Details,Componentproperties - Without
scriptName: Object witherrorsarray andsummaryobject
Restarts a script component.
// Session-based
await core.connect();
await core.restartScript('Main');
await core.disconnect();
// Single-shot
await core.restartScriptSync('Main');Parameters:
componentName(string): Name of the script componentcallback(function, optional): Callback function for session-based method
Collects console logs from a script component.
// Session-based
await core.connect();
const logs = await core.collectLogs('Main');
await core.disconnect();
// Single-shot
const logs = await core.collectLogsSync('Main');Parameters:
componentName(string): Name of the script componentcallback(function, optional): Callback function for session-based method
Returns: Array of log strings with timestamps removed.
Gets the Lua code from a script component.
// Session-based
await core.connect();
const code = await core.getCode('Main');
await core.disconnect();
// Single-shot
const code = await core.getCodeSync('Main');Parameters:
componentName(string): Name of the script componentcallback(function, optional): Callback function for session-based method
Returns: String containing the Lua code.
Updates the Lua code in a script component and returns deployment information.
await core.connect();
const result = await core.updateCode('Main', luaCode);
console.log(`Errors: ${result.deployment.errorCount}`);
console.log(`Logs: ${result.deployment.logs.join(', ')}`);
await core.disconnect();Parameters:
componentName(string): Name of the script componentcode(string): Lua code to deploycallback(function, optional): Callback function
Returns: Object with deployment information including:
deployment.componentName(string): Component namedeployment.codeLength(number): Length of deployed codedeployment.errorCount(number): Number of errors after deploymentdeployment.errorDetails(object): Error details if anydeployment.logs(array): Console logs after deploymentdeployment.timestamp(string): Deployment timestamp
Static method to deploy one script to multiple Q-SYS cores.
const { username, pin } = process.env;
const coreConfigs = [
{ ip: '192.168.1.100', username, pin, systemName: 'Core1' },
{ ip: '192.168.1.101', username, pin, systemName: 'Core2' },
{ ip: '192.168.1.102', username, pin, systemName: 'Core3' }
];
const result = await Core.deployToMultipleCores(
coreConfigs,
'Main',
fs.readFileSync('./scripts/main.lua', 'utf8'),
{ validateFirst: true, rollbackOnError: true }
);Parameters:
coreConfigs(array): Array of core configuration objectscomponentName(string): Name of the script componentcode(string): Lua code to deployoptions(object, optional):validateFirst(boolean): Validate code before deploymentrollbackOnError(boolean): Rollback on deployment errors
Returns: Object with deployment results for each core.
Loads Lua code from a file and deploys it to a component.
await core.connect();
const result = await core.loadScriptFromFile('./scripts/main.lua', 'Main');
console.log(`Deployed: ${result.codeLength} characters, ${result.errorCount} errors`);
await core.disconnect();Parameters:
filePath(string): Path to the Lua filecomponentName(string): Name of the script componentoptions(object, optional): Additional options
Returns: Object with deployment information.
Saves Lua code from a component to a file.
await core.connect();
await core.saveScriptToFile('Main', './backup/main.lua', {
createDir: true,
backup: true
});
await core.disconnect();Parameters:
componentName(string): Name of the script componentfilePath(string): Path where to save the fileoptions(object, optional):createDir(boolean): Create directory if it doesn't existbackup(boolean): Create backup if file exists
Synchronizes a script component with a file.
await core.connect();
// Check sync status without making changes
const status = await core.syncScriptWithFile('Main', './scripts/main.lua', 'check');
console.log(status.message); // "File (3252 chars) and component (3232 chars) differ"
// Force file to component
await core.syncScriptWithFile('Main', './scripts/main.lua', 'push', {
createBackup: true // Create backup before overwriting (default: true)
});
// Force component to file
await core.syncScriptWithFile('Main', './scripts/main.lua', 'pull', {
createBackup: true // Create backup before overwriting (default: true)
});
await core.disconnect();Parameters:
componentName(string): Name of the script componentfilePath(string): Path to the filedirection(string): 'check', 'push', or 'pull'options(object, optional):createBackup(boolean): Create backup files before overwriting (default: true)
Directions:
'check': Compare file and component, return status information'push': Update component with file content (file → component)'pull': Update file with component content (component → file)
Returns: Object with sync information:
success(boolean): Operation success statusaction(string): 'check', 'push', or 'pull'direction(string): Sync direction used (for push/pull)message(string): Descriptive status messageinSync(boolean): Whether file and component are identical (check only)status(string): Sync status - 'in-sync', 'out-of-sync', 'file-missing', 'component-missing', 'both-missing' (check only)fileSample(string): First 100 characters of file (check only, when different)componentSample(string): First 100 characters of component (check only, when different)codeLength(number): Length of synced code (push/pull only)backupPath(string): Path to backup file if created (pull only)
Backup all control scripts from the system to a designated folder.
const { username, pin } = process.env;
const core = new Core({ ip: '192.168.1.100', username, pin });
await core.connect();
// Auto-generated backup directory (default - no timestamp, will overwrite)
const result = await core.backupAllScripts();
// Creates: ./backup_192-168-1-100/
// Auto-generated with timestamp (prevents overwrites)
const result = await core.backupAllScripts(null, { timestamp: true });
// Creates: ./backup_192-168-1-100_2025-11-05T17-33-26-480Z/
// Specific backup directory
const result = await core.backupAllScripts('./backups/core1');
// Advanced backup with options
const result = await core.backupAllScripts('./backups/core1', {
createDir: true, // Create directory if it doesn't exist (default: true)
includeEmpty: false, // Include scripts with empty code (default: false)
systemName: 'Core1', // Custom system name for manifest (default: IP)
timestamp: false // Include timestamp in auto-generated directory names (default: false)
});
console.log(`Backed up ${result.scriptCount} scripts to ${result.backupDir}`);
console.log(`Scripts: ${result.scripts.join(', ')}`);
await core.disconnect();Parameters:
backupDir(string, optional): Directory path where scripts will be saved (default: auto-generated folder)options(object, optional):createDir(boolean): Create backup directory if it doesn't exist (default: true)includeEmpty(boolean): Include scripts with empty/blank code (default: false)systemName(string): Custom system name for manifest (default: uses systemName or IP)timestamp(boolean): Include timestamp in auto-generated directory names (default: false)
Returns: Object with backup information:
success(boolean): Backup operation success statusbackupDir(string): Path to backup directoryscriptCount(number): Number of scripts backed uptotalComponents(number): Total components found in systemmanifest(string): Path to backup manifest filescripts(array): Array of script names that were backed up
Files Created:
{ComponentName}.lua- Individual script filesbackup_manifest.json- Backup metadata and script inventory
Static method to monitor script health across multiple cores.
const { username, pin } = process.env;
const coreConfigs = [
{ ip: '192.168.1.100', username, pin, systemName: 'Core1' },
{ ip: '192.168.1.101', username, pin, systemName: 'Core2' },
{ ip: '192.168.1.102', username, pin, systemName: 'Core3' }
];
const healthReport = await Core.monitorScriptHealth(
coreConfigs,
['Main', 'Module'],
{
includeErrors: true,
includeStatus: true,
includeLogs: true,
logLines: 5
}
);
console.log(`Overall health: ${healthReport.summary.healthPercentage}%`);
console.log(`Cores with issues: ${healthReport.summary.coresWithIssues}`);Parameters:
coreConfigs(array): Array of core configuration objectsscriptNames(array): Array of script names to monitoroptions(object, optional):includeErrors(boolean): Include error informationincludeStatus(boolean): Include status informationincludeLogs(boolean): Include log informationlogLines(number): Number of log lines to include
Returns: Object with health report and summary.
- Always disconnect: Ensure sessions are properly closed
- Use try/finally: Guarantee cleanup even on errors
- Batch operations: Use sessions for multiple operations
- Single-shot for simple tasks: Use sync methods for one-off operations
const { username, pin } = process.env;
const core = new Core({ ip: '192.168.1.100', username, pin });
try {
await core.connect();
// Multiple operations
const components = await core.getComponents();
const errors = await core.getScriptErrors();
const logs = await core.collectLogs('Main');
} catch (error) {
console.error('Session operations failed:', error);
} finally {
await core.disconnect(); // Always cleanup
}- Node.js 14+ (ES modules support)
- Q-SYS Core with QRC enabled
- Network access to Q-SYS Core on port 1710
ISC
Issues and pull requests welcome at GitHub repository.

