Python BLE CLI library for controlling the Godox UL60Bi Lite LED light via Bluetooth Mesh proxy
Project description
Godox UL60Bi Bluetooth Tools
Python library and CLI for controlling a Godox UL60Bi Lite over Bluetooth Mesh.
The package handles the full lifecycle: provisioning a factory-reset light, pushing the application key, and sending brightness/CCT vendor commands — all without the Godox iOS/Android app.
The package has a working CLI for setting brightness and color temperature via Bluetooth Mesh (Telink SDK, proxy mode). It also includes development tools for scanning, inspecting GATT services, parsing BLE captures, and sending raw writes.
Install
uv add godox-ul60bi-bt
Or for local development from this checkout:
uv sync
Quick Start
First-time setup (factory-reset light)
Provision the light (generates fresh mesh keys and saves mesh_state.json):
godox-ul60bi provision
Push the application key to the device:
godox-ul60bi rebind
Control the light:
godox-ul60bi set --brightness 80 --cct 4000
godox-ul60bi on
godox-ul60bi off
That's it. No Android app, no key extraction required.
Subsequent sessions
After first-time setup, mesh_state.json persists the keys and sequence
number. Normal control commands load it automatically:
godox-ul60bi set --brightness 50 --cct 5600
Migrating from the Godox app
If you have already provisioned the light with the Godox app and want to use this library without re-provisioning, import the mesh state from a Telink shared preferences XML (extracted from an Android debug report):
godox-ul60bi setup --import captures/telink_shared.xml
Python API
Use GodoxController as an async context manager:
import asyncio
from godox_ul60bi_bt import GodoxController
async def main() -> None:
async with GodoxController("304BCD50-D2C2-4FA6-A666-F4867E54F267", "mesh_state.json") as light:
await light.power_on()
await light.set_params(brightness=80, cct=4000)
asyncio.run(main())
Provision programmatically:
import asyncio
import dataclasses
import os
from godox_ul60bi_bt.provisioning import ProvisioningSession
from godox_ul60bi_bt.config_session import ConfigSession
async def provision_and_bind(address: str) -> None:
net_key = os.urandom(16)
app_key = os.urandom(16)
# Step 1: provision (factory-reset device must be advertising)
session = ProvisioningSession(
address=address,
net_key=net_key,
key_index=0,
iv_index=0,
unicast_address=0x0002,
)
state = await session.run()
state = dataclasses.replace(state, app_key=app_key.hex())
state.save("mesh_state.json")
# Step 2: push app key (device now advertising on 0x1828)
await ConfigSession(address=address, state=state).run()
asyncio.run(provision_and_bind("304BCD50-D2C2-4FA6-A666-F4867E54F267"))
Commands
godox-ul60bi [-v] {scan,inspect,setup,provision,rebind,on,off,set,raw}
provision
Provision a factory-reset light. Scans for an unprovisioned beacon (Mesh
Provisioning Service, UUID 0x1827), runs the full BT Mesh PB-GATT provisioning
exchange, derives the device key, and saves mesh_state.json.
godox-ul60bi provision
godox-ul60bi provision --address 304BCD50-D2C2-4FA6-A666-F4867E54F267
godox-ul60bi provision --net-key <32-hex> --app-key <32-hex> --output my_state.json
Options:
--address: skip scanning, connect directly--net-key: 32-hex network key (default: random)--app-key: 32-hex application key (default: random)--node-addr: unicast address to assign (default: 2)--output: where to save state (default:./mesh_state.json)--timeout: BLE scan timeout in seconds (default: 10)
After provisioning, run godox-ul60bi rebind to push the app key.
rebind
Push Config App Key Add and Config Model App Bind to the device using the
device key. Required after every provision before vendor commands work.
godox-ul60bi rebind
godox-ul60bi rebind --state my_state.json
set
Send a CCT/brightness vendor command:
godox-ul60bi set --brightness 80 --cct 4000
godox-ul60bi set --brightness 100 --cct 2900 --verbose
--brightness: 0–100--cct: color temperature in Kelvin (2800–6500)--seq-bump N: advance the sequence number by N before sending (RPL recovery)
on / off
godox-ul60bi on
godox-ul60bi off
scan
Scan for provisioned Godox lights (Mesh Proxy Service, UUID 0x1828):
godox-ul60bi scan
godox-ul60bi scan --timeout 10
inspect
Enumerate GATT services, characteristics, and descriptors (read-only):
godox-ul60bi inspect 304BCD50-D2C2-4FA6-A666-F4867E54F267
godox-ul60bi inspect 304BCD50-D2C2-4FA6-A666-F4867E54F267 --format markdown
setup
Import or display mesh state:
godox-ul60bi setup --import mesh_state.json
godox-ul60bi setup --import captures/telink_shared.xml
godox-ul60bi setup --show
State File
mesh_state.json holds everything needed to control the light:
{
"network_key": "98b2e7ef8211c6deca2401adbe52e715",
"app_key": "fa0a2c615756eca3f896ce061ed4d890",
"device_key": "6277be2be27af9818c3d79b62a2a8ae7",
"provisioner_address": 1,
"node_address": 2,
"iv_index": 0,
"sequence_number": 10
}
See mesh_state.example.json for a template with placeholder values.
The state file is loaded automatically from the first of:
--state <path>CLI flagGODOX_UL60BI_BT_STATEenvironment variable./mesh_state.json~/.config/godox-ul60bi-bt/mesh_state.json
How It Works
The UL60Bi Lite is a Bluetooth Mesh node, not a simple BLE peripheral. All control commands are sent as encrypted Bluetooth Mesh Network PDUs over the standard Mesh Proxy service (UUID 0x1828).
Provisioning (first time)
When factory-reset, the device advertises the Mesh Provisioning Service (UUID
0x1827). The provision command runs the PB-GATT exchange:
- Invite → device sends Capabilities
- Start + PublicKey (P-256 ECDH) → device sends PublicKey
- ECDH shared secret computed → Confirmation exchange
- Random exchange → session key + device key derived
- Data (encrypted network key + unicast address) → device sends Complete
The device key is derived from the ECDH session and is used for Config Server messages (App Key Add, Model App Bind).
Rebind (after every provision)
After provisioning, the device knows the network key but has no application key
bound to the vendor model. rebind connects over the Mesh Proxy service and
sends:
- Config App Key Add — pushes the app key, encrypted with the device key
- Config Model App Bind — binds the app key to the Telink vendor model
Vendor Commands
Light control uses a Telink LE vendor model (company ID 0x0211, model ID
0x0000). The access layer opcode is 0x00F011 (3 bytes, LE). The payload
encodes brightness, CCT, and a CRC-8 check byte.
Troubleshooting
Commands appear to have no effect (RPL rejection)
The device silently drops PDUs with a sequence number at or below its stored high-water mark (Replay Protection List). If you used the Godox app before, bump the sequence number:
godox-ul60bi set --seq-bump 50000 --brightness 80 --cct 4000
Or edit sequence_number in mesh_state.json directly.
Proxy Filter Status acknowledgment is missing
Normal behavior. The device only sends Proxy Filter Status ACKs during the original provisioning session. Subsequent sessions silently accept proxy config PDUs. This is logged at DEBUG level, not WARNING.
Need to re-provision
Factory-reset the light (hold the power button until it flashes), then:
godox-ul60bi provision
godox-ul60bi rebind
Development
uv run pytest
uv run ruff check .
uv run ty check
Add dependencies with uv add or uv add --dev.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file godox_ul60bi_bt-1.1.0.tar.gz.
File metadata
- Download URL: godox_ul60bi_bt-1.1.0.tar.gz
- Upload date:
- Size: 42.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
50c2fb5267b7e6f2f7ca8f5b34765152a6a99d956da7cccd0e5c1df33e758038
|
|
| MD5 |
4d32c9b6a94e8000916f53d90094492e
|
|
| BLAKE2b-256 |
00dcb416e45b6dcd89d7d8ff4b7a8629742bd3f9e42ddde59ed03f282104bfdb
|
File details
Details for the file godox_ul60bi_bt-1.1.0-py3-none-any.whl.
File metadata
- Download URL: godox_ul60bi_bt-1.1.0-py3-none-any.whl
- Upload date:
- Size: 52.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
780b64a80fca008f6f706d1b3f5e50cd61fc70c32e34a2ca72c9ad1e4fa935b3
|
|
| MD5 |
293e2297b6cd42c982cb0c4b1965105b
|
|
| BLAKE2b-256 |
138c910b646161392ea673ff0bd727049eb69486a19a91493ce7214c04395d66
|