Skip to content

Python SDK#

The Python package is named enody. It wraps the Rust core with native PyO3 bindings and adds Python-friendly helpers for discovery, WiFi pairing, token storage, firmware updates, JSON data, plotting, and tinygrad optimization. For new host applications and CLI workflows, start with the Rust SDK.

Install#

pip install enody

Color science, plotting, and tinygrad optimization dependencies are included in the package for this branch; no separate science extra is required.

Discover a USB device#

import enody

env = enody.UsbEnvironment()
runtimes = env.runtimes()

if not runtimes:
    raise SystemExit("No Enody devices found.")

runtime = runtimes[0]
host = runtime.host()

print(f"Host {host.identifier()} (v{host.version()})")

Traverse fixtures, sources, and emitters#

for fixture in host.fixtures():
    print("fixture", fixture.identifier())
    for source in fixture.sources():
        print("  source", source.identifier())
        for emitter in source.emitters():
            data = emitter.spectral_data()
            print("    emitter", emitter.identifier(), data.sample_count())

Display built-in modes#

from enody import Configuration, Flux

fixture = host.fixtures()[0]

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

Manual emitter control#

from enody import Configuration, Flux

source = fixture.sources()[0]

for emitter in source.emitters():
    emitter.set_flux(Flux.relative(0.25))

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

Work with sample data#

import enody.data

fixture = enody.data.sample_fixture()
source = enody.data.sample_source()
emitter = enody.data.sample_emitter()

print(fixture.identifier())
print(source.tensor().shape)
print(emitter.tensor().shape)

Sample data is device-shaped JSON loaded into the same Fixture, Source, and Emitter classes used for connected devices. That makes offline experiments look like hardware experiments.

Plot spectral data#

import enody.data

source = enody.data.sample_source()
source.plot_emitter_spectral_distributions(output="emitters.png", display=False)
source.plot_emitter_chromaticity_diagram(output="chromaticity.png", display=False)

WiFi discovery#

import enody

tokens = enody.TokenStore.load().tokens()
wifi = enody.WifiEnvironment(tokens)

for runtime in wifi.runtimes():
    host = runtime.host()
    print(host.identifier(), host.version())

Set up WiFi over USB#

import enody

runtime = enody.UsbEnvironment().runtimes()[0]
host = runtime.host()

for network in host.wifi_scan():
    print(network.ssid(), network.rssi(), network.auth())

host.wifi_join("Studio WiFi", "network-password")
token = runtime.generate_token()
enody.TokenStore.save_token(token)

Generate a WiFi token#

import enody

token = enody.generate_wifi_token(
    on_approval=lambda instruction: print(instruction),
    save=True,
)

The helper discovers a token-generation candidate, requests device approval, verifies the token by reconnecting, and optionally saves it.

This pairing flow is the current integration primitive for saved-token clients. The exact Home Assistant integration surface is still TBD.

Update EP01#

import enody

for target in enody.UpdateTarget.discover():
    print(target.identifier(), target.version())
target = enody.UpdateTarget.discover()[0]
versions = target.available_firmware()
target.update_device(versions[0])

Dependencies#

The package includes:

  • colour-science for color science computations and plotting support.
  • matplotlib for generated spectral plots.
  • tinygrad for tensor operations and optimization workflows.