A pure Lua BTHome BLE advertisement parser with zero external dependencies. Supports both V1 and V2 formats, including encrypted advertisements with AES-128-CCM decryption. This library provides a complete, cross-platform implementation that runs on Lua 5.1, 5.2, 5.3, 5.4, and LuaJIT.
- Zero Dependencies: Pure Lua implementation, no C extensions required
- Portable: Runs on Lua 5.1, 5.2, 5.3, 5.4, and LuaJIT
- Complete: Supports 78+ sensor types from the BTHome specification
- Encryption: AES-128-CCM decryption for encrypted advertisements
- Well-tested: 70+ self-tests with vectors from the official bthome-ble implementation
Download the single-file distribution from the releases page:
bthome.lua- Full library (includes bitn for bitwise operations)bthome-core.lua- Core library only (requires external bitn)
Or clone this repository:
git clone https://github.com/finitelabs/lua-bthome-ble.git
cd lua-bthome-bleAdd the src and vendor directories to your Lua path.
local bthome = require("bthome")
-- Check version
print(bthome.version())
-- Parse an unencrypted V2 advertisement (UUID 0xFCD2)
-- Example: Temperature 25.06°C + Humidity 50.55%
local service_data = "\x40\x02\xca\x09\x03\xbf\x13"
local result, err = bthome.parse(bthome.UUID_V2, service_data)
if result then
print("BTHome Version:", result.device_info.version)
print("Encrypted:", result.device_info.encrypted)
for _, reading in ipairs(result.readings) do
print(string.format("%s: %s %s",
reading.name,
reading.value,
reading.unit or ""))
end
else
print("Parse error:", err)
endOutput:
BTHome Version: 2
Encrypted: false
temperature: 25.06 °C
humidity: 50.55 %
local bthome = require("bthome")
-- 16-byte encryption key (bind_key)
local bind_key = "\x23\x1d\x39\xc1\xd7\xcc\x1a\xb1\xae\xe2\x24\xcd\x09\x6d\xb9\x32"
-- 6-byte MAC address
local mac_address = "\x54\x48\xe6\x8f\x80\xa5"
-- V2 encrypted service data (UUID 0xFCD2)
local service_data = "\x41..." -- encrypted payload
local result, err = bthome.parse(bthome.UUID_V2, service_data, bind_key, mac_address)
-- V1 encrypted service data (UUID 0x181E)
local v1_service_data = "\xfb..." -- encrypted payload
local result, err = bthome.parse(bthome.UUID_V1_ENCRYPTED, v1_service_data, bind_key, mac_address)
if result then
for _, reading in ipairs(result.readings) do
print(reading.name, reading.value)
end
end{
device_info = {
encrypted = false, -- true if advertisement was encrypted
trigger_based = false, -- true for button/event devices
version = 2 -- BTHome version (1 or 2)
},
packet_id = 5, -- optional packet counter
readings = {
{
name = "temperature",
value = 25.06,
unit = "°C",
id = 0x02,
instance = 1 -- instance number for duplicate sensors
},
{
name = "humidity",
value = 50.55,
unit = "%",
id = 0x03,
instance = 1
}
}
}| Category | Sensors |
|---|---|
| Environmental | temperature, humidity, pressure, illuminance, dewpoint, uv_index |
| Air Quality | co2, tvoc, pm2_5, pm10 |
| Power | battery, voltage, current, power, energy |
| Motion | motion, acceleration, gyroscope, rotation, speed |
| Binary | opening, door, window, lock, smoke, tamper, vibration, moisture_detected |
| Volume | volume_liters, volume_ml, volume_flow_rate, gas_volume |
| Distance | distance_mm, distance_m |
| Mass | mass_kg, mass_lb |
| Events | button (press, double_press, long_press), dimmer (rotate_left, rotate_right) |
# Run all tests
make test
# Run specific module tests
make test-parser
make test-crypto
# Run test matrix across Lua versions
make test-matrix
# Check formatting and linting
make check# Build single-file distributions
make build
# Output:
# build/bthome.lua - Full library (includes bitn)
# build/bthome-core.lua - Core only (requires external bitn)BTHome is an open standard for broadcasting sensor data over Bluetooth Low Energy. Key characteristics:
- Service UUIDs:
0x181C- V1 unencrypted0x181E- V1 encrypted0xFCD2- V2 (encryption determined by device_info byte)
- V2 Device Info Byte: Bit 0 = encrypted, Bit 2 = trigger-based, Bits 5-7 = version
- Data Format: Object ID followed by little-endian value bytes
- Encryption: AES-128-CCM with 4-byte MIC
For full specification, see bthome.io.
- Pure Lua performance is slower than native implementations
- No constant-time guarantees for cryptographic operations
This is a pure Lua implementation intended for portability and ease of use. While we implement the algorithms correctly and pass all test vectors, the implementation:
- Cannot guarantee constant-time operations
- Has not been independently audited
- Is significantly slower than native implementations
For production use with encrypted advertisements, consider using native cryptographic libraries for the AES-CCM decryption.
GNU Affero General Public License v3.0 - see LICENSE file for details.
Contributions are welcome! Please ensure all tests pass (make test) and
code passes linting (make check).
- BTHome specification by the BTHome community
- bthome-ble Python reference implementation
- Test vectors derived from the official bthome-ble test suite
