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

thrustmaster-solaris

v0.1.0

Published

LED control and joystick input for the Thrustmaster Solaris Base, over libusb

Readme

thrustmaster-solaris

LED control and joystick input for the Thrustmaster Solaris Base, over libusb. TypeScript, ESM, Linux.

import { Device } from 'thrustmaster-solaris';

const device = await Device.open('044f:042a', { baseline: '#9bff37' });
await device.apply({ button5: '#ff0000', logo: '#0000ff' });
await device.close();

Requirements

Node 20 or newer; developed and tested on Node 25.

Linux. The LED path is plain libusb and would port elsewhere, but reading input resolves the device through /sys/class/hidraw, and the permission setup below is udev — neither has been tried on another OS.

Running the examples straight from .ts needs Node 23.6+ (or 22.6+ with --experimental-strip-types). The library itself is compiled, so it has no such requirement.

Setup

1. udev rule

The LED interface is reached over raw USB, and /dev/bus/usb/* is root-only by default. Edit the rule for your own device ids if they differ:

sudo cp 70-thrustmaster-solaris.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules
sudo udevadm trigger

Reading input needs no rule — logind already grants an ACL on hidraw nodes.

2. Build

npm install
npm run build

| Script | | |---|---| | npm run build | Compile src/ to dist/ | | npm run lint | ESLint | | npm run typecheck | Types, library and examples |

Examples

Node runs TypeScript directly, so these need no build of their own — only the library has to be built.

npm run build
node examples/set-colors.ts   # the ways to set colours, then reset

The input side (button, axis and hat events) has no example yet — see TODO.md.

Two things worth knowing

Every write resets the LEDs you did not set. The device wants the whole LED table on every write and offers no way to read the current one back, so anything you are not explicitly setting becomes the Station baseline colour. If your unit has custom colours stored, the first write flattens them — pass a baseline that matches.

The commit packets probably write flash. Each unit keeps its own profile across power cycles, and the commit pair appears to persist into it. Writes are serialised, rate limited to one per 100 ms, and skipped when they would change nothing. Whether it truly is flash is unconfirmed (PROTOCOL.md §7) — until it is, avoid frame-rate animation.

Design

Nothing about a specific unit is hardcoded. You describe your device — its USB id and idle colour — and pass it in. Any device speaking this protocol works.

Product (abstract)          zones, LED packet layout, input report layout
  └── Solaris               src/products/solaris.ts

Station ──┐                 which product + which device + its idle colour
          ├── Device ─────┬── LedTable ── Color / Zone      staged state
UsbId ────┘               ├── LedWriter                     libusb out
                          ├── HidInputReader ── InputReport hidraw in
                          └── WriteQueue                    serialise, rate limit

Station describes a device; Device is an open connection to one. They are separate because they have different lifetimes — a Station is an immutable value you can build from config with no hardware present, a Device owns a claimed USB interface and an open stream.

Everything device-specific lives on the product, including the wire format, and it drives the types too:

await Device.open('044f:042a', { baseline: '#9bff37' });   // Solaris by default
await Device.open(0x1234, { product: Warthog });           // class or instance

Adding a joystick means subclassing Product in src/products/ — no edits to the library. src/products/solaris.ts is the worked example and the first file to port; docs/PROTOCOL.md explains every value in it.

Status

The output path is verified against hardware: colours apply, per-LED painting works, reset works. Input is verified to open, read and decode, including correctly ignoring the latched toggle switch that sits permanently on.

Not implemented: HandleRing, which needs a separate report variant the protocol doc is itself unsure about.

There are no automated tests yet. Everything above was checked by hand. The byte-level fixture in PROTOCOL.md §4.7 is the obvious starting point and needs no hardware — see TODO.md.

The zone names come from the Windows Control Panel's labels. Which HID button index sits under which labelled LED was never captured — log button events on your own unit rather than assuming index 5 is button5.