https://github.com/finitelabs/lua-bthome-ble
Pure Lua BTHome BLE advertisement parser with AES-128-CCM decryption for encrypted payloads. Supports V1 and V2 formats. No C dependencies, supports Lua 5.1+ and LuaJIT.
https://github.com/finitelabs/lua-bthome-ble
Last synced: about 2 months ago
JSON representation
Pure Lua BTHome BLE advertisement parser with AES-128-CCM decryption for encrypted payloads. Supports V1 and V2 formats. No C dependencies, supports Lua 5.1+ and LuaJIT.
- Host: GitHub
- URL: https://github.com/finitelabs/lua-bthome-ble
- Owner: finitelabs
- License: agpl-3.0
- Created: 2026-01-29T17:32:42.000Z (4 months ago)
- Default Branch: main
- Last Pushed: 2026-03-24T22:38:11.000Z (2 months ago)
- Last Synced: 2026-03-26T03:57:13.020Z (2 months ago)
- Language: Lua
- Homepage:
- Size: 60.5 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# lua-bthome-ble
A pure Lua [BTHome](https://bthome.io) 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.
## Features
- **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
## Installation
Download the single-file distribution from the
[releases page](https://github.com/finitelabs/lua-bthome-ble/releases):
- **`bthome.lua`** - Full library (includes bitn for bitwise operations)
- **`bthome-core.lua`** - Core library only (requires external bitn)
Or clone this repository:
```bash
git clone https://github.com/finitelabs/lua-bthome-ble.git
cd lua-bthome-ble
```
Add the `src` and `vendor` directories to your Lua path.
## Usage
### Basic Example
```lua
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)
end
```
Output:
```
BTHome Version: 2
Encrypted: false
temperature: 25.06 °C
humidity: 50.55 %
```
### Encrypted Advertisements
```lua
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
```
### Result Structure
```lua
{
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
}
}
}
```
### Supported Sensor Types
| 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) |
## Testing
```bash
# 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
```
## Building
```bash
# Build single-file distributions
make build
# Output:
# build/bthome.lua - Full library (includes bitn)
# build/bthome-core.lua - Core only (requires external bitn)
```
## BTHome Protocol
BTHome is an open standard for broadcasting sensor data over Bluetooth Low
Energy. Key characteristics:
- **Service UUIDs**:
- `0x181C` - V1 unencrypted
- `0x181E` - V1 encrypted
- `0xFCD2` - 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](https://bthome.io).
## Current Limitations
- Pure Lua performance is slower than native implementations
- No constant-time guarantees for cryptographic operations
## Security Warning
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.
## License
GNU Affero General Public License v3.0 - see LICENSE file for details.
## Contributing
Contributions are welcome! Please ensure all tests pass (`make test`) and
code passes linting (`make check`).
## Acknowledgments
- [BTHome specification](https://bthome.io) by the BTHome community
- [bthome-ble](https://github.com/Bluetooth-Devices/bthome-ble) Python reference implementation
- Test vectors derived from the official bthome-ble test suite
---
