Skip to main content

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:

  1. --state <path> CLI flag
  2. GODOX_UL60BI_BT_STATE environment variable
  3. ./mesh_state.json
  4. ~/.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:

  1. Invite → device sends Capabilities
  2. Start + PublicKey (P-256 ECDH) → device sends PublicKey
  3. ECDH shared secret computed → Confirmation exchange
  4. Random exchange → session key + device key derived
  5. 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:

  1. Config App Key Add — pushes the app key, encrypted with the device key
  2. 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.

Metadata

Release files for godox-ul60bi-bt 1.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for godox-ul60bi-bt 1.1.0
File Size Uploaded
godox_ul60bi_bt-1.1.0.tar.gz 42.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for godox-ul60bi-bt 1.1.0
File Interpreter ABI Platform
godox_ul60bi_bt-1.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 94.8 kB

Release files / godox_ul60bi_bt-1.1.0.tar.gz

Download URL godox_ul60bi_bt-1.1.0.tar.gz
Size 42.0 kB
Tags Source
SHA-256 checksum
How to use checksums
50c2fb5267b7e6f2f7ca8f5b34765152a6a99d956da7cccd0e5c1df33e758038
BLAKE2b-256 checksum
How to use checksums
00dcb416e45b6dcd89d7d8ff4b7a8629742bd3f9e42ddde59ed03f282104bfdb
Upload date
Uploaded using Trusted Publishing?
What is 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}

Release files / godox_ul60bi_bt-1.1.0-py3-none-any.whl

Download URL godox_ul60bi_bt-1.1.0-py3-none-any.whl
Size 52.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
780b64a80fca008f6f706d1b3f5e50cd61fc70c32e34a2ca72c9ad1e4fa935b3
BLAKE2b-256 checksum
How to use checksums
138c910b646161392ea673ff0bd727049eb69486a19a91493ce7214c04395d66
Upload date
Uploaded using Trusted Publishing?
What is 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}

Release history Release notifications | RSS feed

This release

1.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page