A tested Python library and command-line tool for discovering, reading, and logging illuminance measurements from a UNI-T UT383BT over Bluetooth Low Energy.
The project is hardware-independent in CI: parsing, CSV output, BLE lifecycle, error handling, and the CLI are tested with deterministic fakes. A physical meter is required only for real discovery and capture.
Note
The BLE request and notification format are based on observation of one UT383BT. They are not documented in the public product manual and may vary across firmware revisions.
- Cross-platform BLE through Bleak
- No hard-coded device address or macOS UUID
- Explicit
scan,read,log, and offlineparsecommands - Configurable characteristic UUIDs, command, interval, duration, and write mode
- UTC ISO 8601 and Unix timestamps in CSV output
- Malformed notifications are isolated without stopping a capture
- Guaranteed notification cleanup and BLE disconnection
- Python 3.11-3.14 CI, 97%+ branch coverage, Ruff, CodeQL, and dependency audits
- Python 3.11 or newer
- Bluetooth Low Energy hardware enabled on the host
- A UNI-T UT383BT meter with Bluetooth enabled
- OS permission for the terminal or Python process to use Bluetooth
Bleak 3 supports macOS, Windows 11, Linux with a compatible BlueZ stack, and selected Android environments. This project was originally explored on macOS; device identifiers differ by platform.
From the repository:
git clone https://github.com/ddtdanilo/ut383bt-lux-ble-python.git
cd ut383bt-lux-ble-python
python3 -m venv .venv
source .venv/bin/activate
python -m pip install .For development:
python -m pip install --editable ".[dev]"ut383bt scan --timeout 8Example:
NAME IDENTIFIER RSSI
UT383BT 11111111-2222-3333-4444-555555555555 -48
Use the reported identifier exactly. Linux and Windows commonly show a Bluetooth address; macOS normally shows a UUID.
ut383bt read \
--device "11111111-2222-3333-4444-555555555555" \
--duration 30ut383bt log \
--device "11111111-2222-3333-4444-555555555555" \
--duration 60 \
--output measurements/lux.csvCSV format:
timestamp_utc,epoch_seconds,lux
2026-07-25T06:30:00.123456+00:00,1784961000.123456,250ut383bt parse "3a323530204c55583b"That hexadecimal payload represents :250 LUX;.
All commands are also available through:
python -m ut383bt --help| Purpose | UUID / value |
|---|---|
| Data In characteristic | 0000ff01-0000-1000-8000-00805f9b34fb |
| Data Out characteristic | 0000ff02-0000-1000-8000-00805f9b34fb |
| Periodic request | 0x5E |
| Default request interval | 1 second |
| Observed payload shape | ASCII content between : and ;, containing an integer lux value |
The CLI uses writes without response by default and specifies that choice
explicitly, as recommended for modern Bleak. If a firmware revision exposes only
acknowledged writes, add --write-with-response.
See Protocol notes before changing UUIDs or parser behavior.
import asyncio
from ut383bt import UT383BTClient
def display(measurement):
print(measurement.captured_at.isoformat(), measurement.lux)
async def main():
client = UT383BTClient("YOUR-BLE-IDENTIFIER")
await client.collect(duration=30, callback=display)
asyncio.run(main())The complete public surface is documented in API reference.
python -m pip install --editable ".[dev]"
ruff check .
ruff format --check .
pytest
python -m build
pip-auditNo test connects to a real Bluetooth peripheral. Hardware verification is an explicit manual step described in Contributing.
BLE scans expose nearby device names and identifiers to the local process. CSV
logs reveal when and where measurements were taken. Review
PRIVACY.md, avoid publishing identifiers or sensitive logs, and
never use this utility as the sole input to a safety-critical lighting system.
Released under the WTFPL, Version 2, preserving the license declared by the original scripts.