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

@potatosalad775/eqcaps-device-bridge

v0.2.3

Published

Reads and writes parametric EQ on audio hardware over HID, serial and Bluetooth, behind a transport interface with no browser types.

Readme

@potatosalad775/eqcaps-device-bridge

Reads and writes parametric EQ on audio hardware: FiiO, Walkplay-chipset dongles, Moondrop, KT Micro, Fosi Audio, Qudelix, JDS Labs, Nothing, Tanchjim, Edifier, Audeze and others, every hardware device in the eqcaps database. Protocols from devicePEQ.

  • No platform types. Handlers talk to two small transport interfaces (HidTransport, StreamTransport). Browser transports for WebHID, Web Serial and Web Bluetooth are in @potatosalad775/eqcaps-device-bridge/browser; a native app (an Android Capacitor plugin, say) implements the interfaces over its own USB stack.
  • Driven by the database. The bridge knows how to talk to a device, not what it accepts or which device it is. The client matches a connected device to its eqcaps profile; the profile says what the device accepts (fit and complete with @potatosalad775/eqcaps-core before writing) and, in its protocol, which handler drives it with which settings. protocolForMatches(matches) gives the protocol of the most specific match that has one this bridge can drive; protocolOf(profile) reads one profile's or index entry's. A device added to the database on a protocol the bridge already speaks needs no new bridge release.
  • Writes exactly what it is given. No clamping, padding, type conversion or compensation. A value the wire can't carry or a type the protocol has no code for is a BridgeError, before anything is sent.
import { createClient } from '@potatosalad775/eqcaps-client';
import { complete, fit } from '@potatosalad775/eqcaps-core';
import {
	guessProtocol,
	identityOf,
	openDevice,
	protocolForMatches
} from '@potatosalad775/eqcaps-device-bridge';
import { requestHidDevice } from '@potatosalad775/eqcaps-device-bridge/browser';

const client = createClient();
const index = await client.loadIndex();
// The browser's chooser, offering the database's devices; null if cancelled.
const transport = await requestHidDevice({ entries: index?.profiles ?? [] });
if (transport) {
	const { best, matches } = await client.matchDevice(identityOf(transport));
	const profile = best ? await client.loadProfile(best.id) : null;
	const protocol =
		protocolForMatches(matches)?.protocol ?? guessProtocol(transport.vendorId);
	if (protocol) {
		const device = openDevice(transport, protocol, profile ? { profile } : {});

		// Read: written values, as the device holds them.
		const state = await device.pull();

		// Write: what the user wants, fitted to the device, every band filled. With the read-back
		// as hints, bands the user left empty keep what the device holds, written flat.
		if (profile) {
			const result = fit(profile, wantedFilters, wantedPreamp);
			const filters = complete(profile, result.slots, { hints: state.filters }).filters;
			const preamp = profile.preamp.mode === 'manual' ? result.preamp : undefined;
			await device.push({ filters, ...(preamp === undefined ? {} : { preamp }) });
		}
	}
}

Offline, match against an embedded bundle.json with the client's matchDevice instead. Serial ports and BLE devices are chosen first and opened once the protocol is known: requestSerialPort() and requestBleDevice() return the device's identity and an open(protocol). A Bluetooth serial port shows only its service class, which several devices share: ask the user which device it is.

device.capabilities says what the protocol can do (canRead, canWrite, readsPreamp, readsSlot, writesPreamp, writesSlot, needsBandCount, the filter types it has wire codes for, the wire grid it can carry, preset slots, disconnectOnSave, experimental). A push with a preamp or slot the protocol can't write is refused, not sent without it, and so is a pull of a slot the protocol can't read. Each handler's codec turns a push request into frames and back without a device, for checking what a value becomes on the wire. analyzeCodec(protocol) works out offline what a protocol's writes can carry: the band counts one write takes, the filter types with wire codes, and each field's wire range and resolution as an eqcaps domain. That is handler-code knowledge (SPEC §10): what a device can be sent, not what it accepts. handlerCodeUrl(handler, commit) is the source ref for it.

Handlers and their options

A profile's protocol (eqcaps SPEC §9) names one of these handlers, with the options its device needs. Byte options may be written as hex strings ("0x21"). protocolProblem(protocol) says why a block can't be driven: an unknown handler (data newer than this bridge) or option.

| Handler | Transports | Options | | --- | --- | --- | | fiio-usb-hid | HID | reportId (default 7), saveCommand (default 0x19; 0x21 on newer models) | | walkplay-hid | HID | defaultSlot (slot of a write that names none, default 101) | | moondrop-usb-hid | HID | – | | moondrop-old-fashioned-hid | HID | – | | conexant-usb-hid | HID | – | | ktmicro-usb-hid | HID | baseRegister (default 0x26), bandRegisters ([{ "freq", "q" }] per band, for models that skip or reorder registers), customSlot (default 3) | | fosi-audio-usb-hid | HID | reportId (default 1), bandwidth (default 0), defaultSlot (default 7) | | qudelix-usb-hid | HID | – (the report comes from the descriptor) | | jds-labs-usb-serial | serial | – | | nothing-usb-serial | serial | customSlot (default 5) | | fiio-usb-serial | serial | saveCommand (default 0x19) | | fiio-f110 | serial, BLE | – | | tanchjim-rita-serial | serial | – | | moondrop-edge-serial | serial | – | | edifier-serial | serial | – | | airoha | serial, BLE | – |

The rest of the block is the same for every handler: presets ([{ "id", "name" }], the device's EQ memories), disconnectOnSave, baudRate (serial; default 115200 over USB, 9600 over Bluetooth) and experimental. A preset marked "bypass": true is the one that turns the EQ off: setEnabled(false) selects it, and reading it back reports no current slot. Only fiio-usb-hid and ktmicro-usb-hid turn the EQ off with a preset (KT Micro's default is slot 2); the others refuse a bypass preset. A new option goes in src/protocols.ts (OPTIONS) and this table.

Errors are BridgeErrors with a code: unsupported-type, unrepresentable, unsupported, timeout, bad-response, rejected, transport or invalid-request.

License: MIT; see NOTICE.md for the projects it is ported from.