Skip to content

JavaScript SDK#

The JavaScript package is named @enody/enody. It targets modern ESM applications and provides the browser and Node.js SDK surface for EP01 discovery, control, spectral data, optimization helpers, and firmware updates.

The package uses conditional exports:

  • Browser builds use WebSerial.
  • Node.js scripts use the Node serial backend.
  • The same @enody/enody import path works in both environments.

Install#

npm install @enody/enody

The package requires Node.js 18 or newer for Node-based tooling.

Discover and connect#

import { UsbEnvironment } from '@enody/enody';

const environment = new UsbEnvironment();
const runtimes = await environment.runtimes();

if (runtimes.length === 0) {
  throw new Error('No Enody devices found.');
}

const runtime = runtimes[0];
const host = await runtime.host();

console.log(`Host ${host.identifier()} (v${host.versionString})`);

In browser apps, UsbEnvironment.runtimes() uses WebSerial. If the page has no previously granted ports, the SDK can prompt the user to pick an EP01 serial device. In Node.js, set ENODY_PORT when you need to target a specific serial port.

Traverse fixtures, sources, and emitters#

const fixtures = await host.fixtures();

for (const fixture of fixtures) {
  console.log('fixture', fixture.identifier());

  for (const source of await fixture.sources()) {
    console.log('  source', source.identifier());

    for (const emitter of await source.emitters()) {
      const data = await emitter.spectralData();
      console.log('    emitter', emitter.identifier(), data.sampleCount());
    }
  }
}

Device-backed methods are asynchronous because they may issue protocol commands over USB serial.

Display built-in modes#

import { Configuration, Flux } from '@enody/enody';

const fixture = (await host.fixtures())[0];

await fixture.display(Configuration.blackbody(4000), Flux.relative(0.8));
await fixture.display(Configuration.chromatic(0.3127, 0.3290), Flux.relative(0.5));

Convenience helpers are available on fixtures:

await fixture.setCCT(4000, 0.8);
await fixture.setChromaticity(0.3127, 0.3290, 0.5);
await fixture.setManual(1.0);

Manual emitter control#

import { Configuration, Flux } from '@enody/enody';

const source = (await fixture.sources())[0];

for (const emitter of await source.emitters()) {
  await emitter.setFlux(Flux.relative(0.25));
}

await fixture.display(Configuration.manual(), Flux.relative(1.0));

WiFi setup#

WiFi setup runs over an already trusted USB or WebSerial connection:

const networks = await host.wifiScan();
console.log(networks.map((network) => network.network?.ssid));

await host.wifiJoin('Studio WiFi', 'network-password');

Pass an empty password for open networks. Lower-level networkScan() and networkJoin() helpers are also exported for non-WiFi network variants.

Runtime settings and presets#

The SDK can read and write public runtime settings, including the EP01 configuration preset setting used by host tooling:

import { Configuration } from '@enody/enody';

const presets = await runtime.configurationPresets();
presets.push(Configuration.blackbody(2700));

await runtime.setConfigurationPresets(presets);

Diagnostics#

Transport diagnostics are quiet by default. During hardware bring-up or support sessions, pass a console-like logger or debug: true:

const environment = new UsbEnvironment({ logger: console });

Command timeouts reject with code: 'ENODY_COMMAND_TIMEOUT'. Device errors include deviceError and command fields for application-level handling.

Work with sample data#

The package bundles the same fixture-shaped sample data used by the other SDKs:

import { sampleFixture, sampleSource, sampleEmitter } from '@enody/enody';

const fixture = sampleFixture();
const source = sampleSource();
const emitter = sampleEmitter();

console.log(fixture.identifier(), source.identifier(), emitter.identifier());

Optimization helpers#

The JavaScript SDK includes typed-array helpers for spectral matching, chromaticity work, blackbody spectra, plant band metrics, and optimizer loops.

import {
  SpectralOptimizer,
  cieXAction,
  cieYAction,
  cieZAction,
  sampleSource,
} from '@enody/enody';

const source = sampleSource();
const emitters = await source.emitters();

const spdMatrix = new Float32Array(emitters.length * 401);
for (let i = 0; i < emitters.length; i += 1) {
  const values = (await emitters[i].spectralData()).values();
  spdMatrix.set(values, i * 401);
}

const optimizer = new SpectralOptimizer({
  spdMatrix,
  numEmitters: emitters.length,
  cieX: new Float32Array(cieXAction()),
  cieY: new Float32Array(cieYAction()),
  cieZ: new Float32Array(cieZAction()),
});

optimizer.setTargetChromaticity(0.3127, 0.3290);
optimizer.step();

Update EP01#

import { UpdateTarget } from '@enody/enody';

const [target] = await UpdateTarget.discover();
const versions = await target.availableFirmware();

await target.updateDevice(versions[0], {
  onLog: console.log,
  verify: true,
});

By default, updater helpers fetch manifests from https://firmware.enody.lighting. In local development, pass baseUrl to point at a firmware proxy.

API overview#

Primary exports include:

  • UsbEnvironment, Runtime, Host, Fixture, Source, Emitter
  • Configuration, Flux, Version
  • Network, NetworkCredentials, WifiAuth, WifiNetwork
  • sampleFixture, sampleSource, sampleEmitter
  • SpectralOptimizer, GPUCompute, computeChromaticity, computeSSI
  • UpdateTarget, ESPFlasher
  • CONFIGURATION_PRESETS_KEY, SENSOR_DATA_STREAMS_KEY