npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mikejobson/qsys-lib

v0.3.1

Published

QsysLib is an Angular service for communicating with QSC Q-Sys cores over WebSocket using the QRC (Q-Sys Remote Control) protocol.

Downloads

53

Readme

QsysLib

QsysLib is an Angular service for communicating with QSC Q-Sys cores over WebSocket using the QRC (Q-Sys Remote Control) protocol.

Installation

Install the library from npm:

npm install @mikejobson/qsys-lib

You can view the package on npm here: https://www.npmjs.com/package/@mikejobson/qsys-lib

Setup

  1. Import the QsysLibModule in your app module:
import { QsysLibModule } from "@mikejobson/qsys-lib";

@NgModule({
  imports: [
    QsysLibModule,
    // other imports
  ],
  // ...
})
export class AppModule {}
  1. Inject the QsysLibService in your components:
import { QsysLibService } from "@mikejobson/qsys-lib";

@Component({
  // ...
})
export class YourComponent {
  constructor(private qsys: QsysLibService) {}
}

Basic Usage

Connecting to a Q-Sys Core

// Connect directly with a raw WebSocket URL - no automatic formatting
this.qsys.connect("wss://192.168.1.100/qrc");

// For convenience, you can use the formatWebsocketUrl helper:
// 1. Connect with an IP address or hostname
const formattedUrl = QsysLibService.formatWebsocketUrl("192.168.1.100");
this.qsys.connect(formattedUrl);
// Connects to "wss://192.168.1.100/qrc"

// 2. Connect using a path - uses the current host with the specified path
const pathUrl = QsysLibService.formatWebsocketUrl("/api/qrc");
this.qsys.connect(pathUrl);
// Formats to "wss://current.host.com/api/qrc" or "ws://current.host.com/api/qrc"

// 3. Preserve existing protocols
const wsUrl = QsysLibService.formatWebsocketUrl("ws://my-qsys-core.example.com/qrc");
this.qsys.connect(wsUrl); // Uses ws: (unsecure)

// 4. Convert HTTP URLs to WebSocket URLs
const httpUrl = QsysLibService.formatWebsocketUrl("http://my-qsys-core.example.com/qrc");
this.qsys.connect(httpUrl); // Converts to "ws://"

// Get the current WebSocket URL
console.log(this.qsys.websocketUrl);

// Set the WebSocket URL directly (doesn't connect automatically)
this.qsys.websocketUrl = "wss://another-core.example.com/qrc";
this.qsys.connect(this.qsys.websocketUrl);

// Optional parameter for maximum reconnection attempts (default is 0 - infinite attempts)
this.qsys.connect("wss://192.168.1.100/qrc", 5);

// Monitor connection status
this.qsys.getConnectionStatus().subscribe((status) => {
  if (status.connected) {
    console.log("Connected to Q-Sys Core");
    console.log("Engine status:", status.engineStatus);

    // Check if the design has changed since last connection
    if (status.newDesign) {
      console.log("The Q-Sys design has changed, refreshing components");
      // Reload your components here as needed
    }
  } else {
    console.log("Disconnected from Q-Sys Core");
    if (status.noReconnect) {
      console.log("No reconnection will be attempted");
    }
  }
});

// Disconnect when done
this.qsys.disconnect();

Getting Engine Status

this.qsys.getEngineStatus().subscribe((status) => {
  if (status) {
    console.log("Design name:", status.DesignName);
    console.log("Design Code:", status.DesignCode);
    console.log("Platform:", status.Platform);
    console.log("Status:", status.Status.String);
  } else {
    console.log("No engine status available");
  }
});

Working with Components

When the service connects to the api for the first time, the service requests all available components and controls from the websocket connection. The components are cached and monitored using a default change group named 'auto'. This is also set to poll after controls are changed locally, and listen at a default rate for external changes.

If the connection drops it should re-establish the change groups automatically as long as the design code is the same. If the design code has changed, note that all objects cached are removed and you should get the component and controls again. The old objects of these will not update otherwise or may be non-existant in the design.

// Get all components in the design
const components = await this.qsys.getAllComponents();
components.forEach((component) => {
  console.log(`Component: ${component.name}, Type: ${component.type}`);
});

// Get a specific component by name
const mixer = await this.qsys.getComponent("MainMixer");
if (mixer) {
  console.log(`Found mixer: ${mixer.name}`);

  // Get component properties
  console.log("Properties:", mixer.properties);

  // Get controls
  mixer.controls.forEach((control) => {
    console.log(`Control: ${control.name}, Type: ${control.type}, Value: ${control.value}`);
  });

  // Get a specific control
  const fader = mixer.getControl("fader1");
  if (fader) {
    console.log(`Current fader value: ${fader.value}`);
  }
}

Controlling Components

// Change a control's value
const mixer = await this.qsys.getComponent("MainMixer");
const gain = mixer?.getControl("input.1.gain");
if (gain) {
  // Set value directly to -20dB
  await gain.setValue(-20);

  // With ramping (time in seconds), sets value to 0dB over 2.5 seconds
  await gain.rampValue(0, 2.5);

  // Set position (for controls that support it, values between 0-1) of gain to half way
  await gain.setPosition(0.5); // 50%

  // With ramping
  await gain.rampPosition(0.75, 3.0); // Ramp to 75% over 3 seconds
}

const mute = mixer?.getControl("input.1.mute");
if (mute) {
  // Read the mute value as a Boolean
  var muteValue: Boolean = gain.value;

  // Set the value
  await mute.setValue(true);
}

Subscribing to Control Changes

// Subscribe to updates for a specific control
const mixer = await this.qsys.getComponent("MainMixer");
const fader = mixer?.getControl("input.1.gain");
if (fader) {
  fader.updated.subscribe((control) => {
    console.log(`Fader updated: ${control.value}`);
  });

  // Subscribe to all controls in a component
  mixer.updated.subscribe((controls) => {
    console.log(
      "Updated controls:",
      controls.map((c) => c.name)
    );
  });
}

Using Direct Commands

Direct commands allow asynchronous comms directly to the QRC protocol rather than using the object methods above.

Note these methods don't return object classes with notifications and control methods, but rather just the data which may be easier for simple tasks.

// Send a command and get the response
try {
  const response = await this.qsys.sendCommandAsync("Component.GetComponents", {});
  console.log("Components:", response);
} catch (error) {
  console.error("Error:", error);
}

// You can also use the getComponents method to receive data for each component.
try {
  const response = await this.qsys.getComponents();
  console.log("Components:", response);
} catch (error) {
  console.error("Error:", error);
}

// Alternatively you can include the controls in there also
try {
  const response = await this.qsys.getComponents(true);
  console.log("Components with controls:", response);
} catch (error) {
  console.error("Error:", error);
}

// Get controls for a specific component
try {
  const controls = await this.qsys.getControls("MainMixer");
  console.log("Controls:", controls);

  // Controls are sorted by name and contain properties like:
  // Name, Type, Value, ValueMin, ValueMax, String, Position, Direction
} catch (error) {
  console.error("Error:", error);
}

// Set a component's control value directly
try {
  // Set a gain control to -10 dB
  await this.qsys.setComponentValue("MainMixer", "input.1.gain", -10);

  // Set a gain control to -20 dB with a 2 second ramp
  await this.qsys.setComponentValue("MainMixer", "input.1.gain", -20, 2);

  // Set a mute control to true
  await this.qsys.setComponentValue("MainMixer", "input.1.mute", true);
} catch (error) {
  console.error("Error:", error);
}

// Set a component's position directly (values between 0-1)
try {
  // Set a fader to 50%
  await this.qsys.setComponentPosition("MainMixer", "input.1.gain", 0.5);

  // Set a fader to 75% with a 3 second ramp
  await this.qsys.setComponentPosition("MainMixer", "input.1.gain", 0.75, 3);
} catch (error) {
  console.error("Error:", error);
}

Change Groups

// Create a change group with specific controls
const groupId = "myGroup";
await this.qsys.changeGroupAddControls(groupId, "MainMixer", ["fader1", "mute1"]);

// Poll the change group manually
const changes = await this.qsys.changeGroupPoll(groupId);
console.log("Changes:", changes);

// Set up auto-polling (rate in seconds)
await this.qsys.changeGroupAutoPoll(groupId, 0.5);

// Listen for change group updates
this.qsys.getChangeGroupUpdates().subscribe((update) => {
  if (update.changeGroupId === groupId) {
    console.log("Updates:", update.changes);
  }
});

Building and Publishing

This library is automatically built and published to npm using GitHub Actions when changes are pushed to the main branch.

Automated Publishing Workflow

  1. When changes are pushed to the main branch that affect files in the projects/qsys-lib/ directory, a GitHub Actions workflow is triggered.
  2. The workflow builds the library and checks if the version in package.json has been incremented.
  3. If the version has been updated, the workflow automatically publishes the new version to npm.

Contributing Changes

If you're contributing changes to this library:

  1. Make your changes in a feature branch
  2. Update the version in projects/qsys-lib/package.json according to semver guidelines:
    • Patch version for backwards compatible bug fixes (0.2.2 → 0.2.3)
    • Minor version for backwards compatible new features (0.2.2 → 0.3.0)
    • Major version for breaking changes (0.2.2 → 1.0.0)
  3. Create a pull request targeting the main branch
  4. Once merged, the GitHub Actions workflow will automatically publish the new version

Manual Building

To build the library locally for testing:

ng build qsys-lib

This command will compile the library, and the build artifacts will be placed in the dist/ directory.

Running unit tests

To execute unit tests with the Karma test runner, use the following command:

ng test

Additional Resources

For more information on using the Angular CLI, including detailed command references, visit the Angular CLI Overview and Command Reference page.