A Node.js + TypeScript + Effect toolkit for controlling a Godox TL30 over BLE
A Node.js + TypeScript + Effect toolkit for controlling
a Godox TL30 over BLE. Monorepo with a core library, native BLE mesh transport,
a CLI, and a Homebridge plugin for HomeKit integration.
| Path | Package name | Role |
|---|---|---|
packages/core/ |
@godox-tl/core |
Domain types, LightController service, registry, factory conveniences, and an experimental DMX transport kept out of the main TL30 path. |
packages/cli/ |
@godox-tl/cli |
godox binary built on @effect/cli — scan / lights / provision / rebind / set / off / rename / forget. |
packages/mesh/ |
@godox-tl/mesh |
Primary native Node BLE transport for TL30: provisioning, rebind, CCT, HSI, RGBW, FX, off, and scan via @stoprocent/noble. |
packages/homebridge/ |
homebridge-godox-tl |
Homebridge dynamic platform plugin. CCT/color/presets over HomeKit, registry-first startup, periodic scan, and auto-provision/re-provision. |
Core stays free of BLE deps; consumers compose core + mesh.
| Model | DMX in | BLE (Godox Light app) | Works via this library |
|---|---|---|---|
| TL30 | no | yes | yes, tested locally |
The Godox Light app uses encrypted Bluetooth SIG Mesh (com.godox.ble.mesh,
Telink stack). For TL30 the only remote-control path is BLE Mesh.
@godox-tl/mesh is the implementation used by the CLI and Homebridge plugin.
Other Godox app-controlled fixtures may use similar BLE protocol pieces, but
this repo should not be read as a compatibility claim for hardware not tested
here. The known target is the TL30.
For CLI users after the package is published:
vp add -g @godox-tl/cli
# or: pnpm add -g @godox-tl/cli
godox --help
For Homebridge users after the plugin is published:
vp add -g homebridge-godox-tl
# or: pnpm add -g homebridge-godox-tl
For library users:
vp add @godox-tl/core @godox-tl/mesh effect
# or: pnpm add @godox-tl/core @godox-tl/mesh effect
For local development:
Note: You need Vite+ installed for the commands below.
vp install
Install the CLI globally:
vp add -g @godox-tl/cli
# or: pnpm add -g @godox-tl/cli
godox --help
Then use the godox binary directly:
godox scan # list nearby BLE devices
godox lights # list registered lights
godox provision <addr> --name kitchen # one-step provision + rebind + register
godox set kitchen --brightness 50 --cct 4500
godox hsi kitchen --brightness 100 --hue 0 --saturation 100
godox rgbw kitchen --brightness 10 --red 255
godox fx kitchen --brightness 100 --effect 2 --level 1
godox off kitchen
godox rename kitchen office
godox forget kitchen
Per-light state files live at ~/.config/godox-tl/states/<name>.json; the
registry at ~/.config/godox-tl/registry.json. --help prints autogenerated
docs for every subcommand.
From this workspace, use vp run @godox-tl/cli#godox -- <subcommand> instead
of a global install.
The legacy demo (packages/cli/src/demo.ts) is kept as an internal playground,
not as part of the TL30 BLE workflow.
homebridge-godox-tl is a dynamic platform plugin. Drop this into your
Homebridge config.json:
{
"platforms": [
{
"platform": "GodoxTL",
"name": "Godox TL",
"autoProvision": false,
"autoProvisionOnStartup": false,
"discoveryFilters": ["^GD_LED$"],
"scanIntervalSeconds": 60
}
]
}
Discovery is registry-first: the plugin loads
~/.config/godox-tl/registry.json (or a custom registryPath) at startup and
exposes each entry as a Service.Lightbulb with On / Brightness /
ColorTemperature characteristics. The startup BLE scan does not provision by
default. To provision factory-reset lights from Homebridge, opt in with
autoProvisionOnStartup: true for a one-shot startup provisioning pass, or
autoProvision: true to scan every scanIntervalSeconds; matching lights are
added as HomeKit accessories with the nameTemplate (default
godox-{shortAddr}).
Set handlers are debounced 100 ms so HomeKit slider drags don’t flood the
mesh. Mesh sends are serialized per controller so concurrent HomeKit updates
reserve sequence numbers in order.
Auto-provision scan failures are surfaced in the Homebridge log and retried on
the next scan interval; they are not treated as an empty scan result.
If a previously provisioned light is factory-reset, Homebridge can
auto-provision it again when it appears as an unprovisioned BLE candidate,
even if the old address is still in the accessory cache.
# Factory-reset the light first (Bluetooth menu → RESET).
godox provision <BLE-address> --name kitchen
This runs provision + rebind + register in one step. The address is the
CoreBluetooth UUID on macOS, a real MAC on Linux — godox scan prints both.
import { Effect } from "effect";
import { Domain, getLight, LightController } from "@godox-tl/core";
import { makeNodeMeshLayer } from "@godox-tl/mesh";
const program = Effect.gen(function* () {
const entry = yield* getLight("kitchen");
const layer = makeNodeMeshLayer({
address: entry.address,
statePath: entry.statePath,
});
const light = yield* LightController;
yield* light.send(
Domain.Cct.make({
brightness: Domain.pct(75),
temperature: Domain.kelvin(4200),
}),
);
}).pipe(Effect.provide(layer));
LightCommand is a tagged union:
Off — power off.Cct { brightness: Percent, temperature: Kelvin } — bi-color tube mode.Hsi { brightness, hue, saturation } — HSI color mode.Rgbw { brightness, red, green, blue, white } — RGBW color mode.Fx { brightness, effect, subtype, filter } — FX mode; mesh maps subtype as the observed level/speed byte.Hsi/Rgbw/Fx are supported by @godox-tl/mesh.
@godox-tl/mesh — Native BLE TransportNative transport for TL30 control. The Homebridge plugin can run on a Raspberry
Pi without a Python runtime.
Milestones:
@stoprocent/noble wrapper, Mesh Proxy Service 0x1828, SAR.@godox-tl/mesh.Crypto and PDU codecs are validated against captured wire-byte fixtures at
packages/mesh/tests/fixtures/ — 30 set + 1 off, byte-exact round-trips.
godox scan shows it as[unprovisioned].godox provision <addr> --name <name>.godox lights shows the light and the state file path.godox set <name> --brightness 50 --cct 4200.godox hsi <name> --brightness 80 --hue 210 --saturation 70.godox rgbw <name> --brightness 20 --red 255.godox fx <name> --brightness 80 --effect 2 --level 1.godox off <name>.vp install # workspace install
vp check # format + lint + type check (everything)
vp test # vitest, every package
vp run @godox-tl/cli#godox -- <subcommand>
To work in a single package:
cd packages/core && vp test
MIT