Documentation
Developer reference for Puffco Peak Pro and Proxy BLE telemetry and control.
Introduction
puffco-py is a standalone, lightweight, asynchronous Python library for communicating with Puffco Peak Pro (V1, V2, Limited Editions) and Puffco Proxy vaporizers using Bluetooth Low Energy (BLE).
Modern Puffco devices use a proprietary firmware protocol called the Lorax Virtual File System (VFS). Rather than using fixed GATT characteristics for every sensor, Puffco multiplexes virtual file paths over a single write/notify characteristic pair. puffco-py implements this protocol natively, providing:
- Low latency streaming — Adaptive polling at up to ~16 FPS (60ms) during active heating sessions.
- Cryptographic authentication — Automatically solves the SHA-256 access seed challenge required by modern firmware.
- Desktop GUI friendly — Dedicated background-worker thread client (
ThreadedPuffcoClient) for Tkinter, PyQt, and macOS status bar widgets. - Hardware telemetry — Live bowl temperature, battery %, charging status, chamber detection (3D, 3DXL, Proxy), and lifetime dab counter.
Installation
Requires Python 3.9 or higher. Install from PyPI or build from source.
Standard Install (PyPI)
pip install puffco-py
Optional Extras
Include CLI formatting and macOS menu bar widget dependencies:
pip install "puffco-py[cli,gui]"
From Source
git clone https://github.com/SoyMilkIsOk/puffco-py.git
cd puffco-py
pip install -e .
Hardware & Platform Support
| Device | Chambers | Firmware | Status |
|---|---|---|---|
| Peak Pro V2 | 3DXL, Standard 3D | v5.0–v5.4.x | ✓ Supported |
| Peak Pro V1 | Standard 3D, Ceramic | v4.x, v5.x | ✓ Supported |
| Puffco Proxy | Proxy 3D Chamber | All production | ✓ Supported |
macOS: Enable Bluetooth for your terminal in System Settings > Privacy & Security > Bluetooth.
Linux: Ensure
bluez is installed. You may need to add your user to the bluetooth group.
Async Python Quickstart
PuffcoClient is an async context manager powered by asyncio and bleak. It auto-discovers the nearest device, authenticates, and starts telemetry.
import asyncio
from puffco_py import PuffcoClient
async def main():
async with PuffcoClient() as client:
print(f"Connected to: {client.telemetry.device_name}")
print(f"Chamber: {client.telemetry.chamber_name} | Battery: {client.telemetry.battery_pct}%")
def on_telemetry(t):
print(f"\rTemp: {t.live_temp_f:.1f}°F | State: {t.state_name}", end="")
client.add_telemetry_listener(on_telemetry)
await client.start_telemetry_stream()
await asyncio.sleep(15)
asyncio.run(main())
Threaded / GUI Quickstart
For desktop GUIs (Tkinter, PyQt, PySide) or synchronous scripts, use ThreadedPuffcoClient. It runs the async BLE event loop on a background thread and exposes simple synchronous methods:
import time
from puffco_py import ThreadedPuffcoClient
client = ThreadedPuffcoClient()
client.add_telemetry_listener(lambda t: print(f"Live Temp: {t.live_temp_f:.1f}°F"))
client.start()
if client.wait_connected(timeout=10.0):
print(client.telemetry.summary())
client.set_profile(slot=1)
client.start_session()
time.sleep(10)
client.boost()
time.sleep(10)
client.stop_session()
client.stop()
CLI Commands
The package includes an out-of-the-box CLI utility named puffco-py:
| Command | Options | Description |
|---|---|---|
puffco-py scan |
-t, --timeout <sec> |
Scan BLE advertisements for nearby Puffco devices. |
puffco-py monitor |
--mac <addr> |
Live stream an interactive terminal HUD with temp, battery, and state. |
puffco-py info |
--mac <addr> |
Dump hardware serial, firmware version, dab count, and profiles. |
puffco-py sesh <action> |
start, stop, boost |
Send immediate heating commands to the connected device. |
--mac AA:BB:CC:DD:EE:FF to target a specific unit.
macOS Menu Bar Widget
A native macOS status bar app is provided in examples/04_mac_menu_widget.py (powered by rumps):
pip install "puffco-py[gui]"
python3 examples/04_mac_menu_widget.py
The widget displays live bowl temperature in your menu bar and provides one-click dropdown actions to start, boost, or abort sessions.
Features
- Lorax VFS Protocol — Native support for Puffco firmware using Lorax Virtual File System sequence multiplexing and SHA-256 challenge-response authentication.
- Sub-100ms Streaming — Adaptive polling engine streams ~16 FPS (60ms) during active heating and throttles to 250ms when idle.
- Thread-Safe & GUI Ready —
ThreadedPuffcoClientfor synchronous UI toolkits (Tkinter, PyQt, PySide) without blocking dispatch threads. - macOS Status Bar Widget — Ready-to-run native menu bar app powered by
rumps, showing live temps and one-click session triggers. - Auto-Discovery — Scans BLE advertisements and filters nearest devices by manufacturer OUI and Lorax GATT UUIDs.
- Full Session Control — Start sessions, abort, trigger heat boosts, change profiles, set temperatures, and toggle stealth mode.
Python API Reference
PuffcoClient Methods
| Method | Return | Description |
|---|---|---|
async connect(timeout=10.0) |
bool |
Connects via Bleak GATT and solves the Lorax SHA-256 access seed challenge. |
async disconnect() |
None |
Gracefully terminates GATT connection and stops polling. |
async start_session() |
None |
Initiates a heating session using the active profile. |
async stop_session() |
None |
Aborts an active heating session immediately. |
async boost() |
None |
Applies heat boost (+15s duration and +10°F). |
async set_profile(slot: int) |
bool |
Switches active profile slot (0–3). |
async set_temperature(temp_f, slot=None) |
bool |
Sets target temperature for the active profile (or specified slot). |
async set_profile_duration(slot, seconds) |
bool |
Sets session timer duration (15–180s) for a profile slot. |
async set_profile_name(slot, name) |
bool |
Sets display name (up to 16 chars) for a profile slot. |
async save_profile(slot, profile) |
bool |
Atomically updates name, temperature, and duration for a profile. |
async set_boost_temperature(temp_f) |
bool |
Configures heat boost temperature increment in °F (+5 to +50°F). |
async set_boost_duration(seconds) |
bool |
Configures heat boost session extension in seconds (+5 to +60s). |
async set_stealth_mode(enabled: bool) |
bool |
Toggles device mood lights on or off. |
add_telemetry_listener(callback) |
None |
Registers a callback(telemetry) called on state changes. |
Telemetry Model
Accessible via client.telemetry:
| Property | Type | Description |
|---|---|---|
device_name |
str |
Configured user device name. |
device_model |
str |
Identified hardware model: "Peak Pro (V2)", "Peak Pro (V1)", "Puffco Proxy", "Puffco Pivot". |
live_temp_f |
float |
Current real-time bowl temperature in °F. |
target_temp_f |
float |
Target temperature of the active profile. |
battery_pct |
int |
Battery state of charge (0–100%). |
is_charging |
bool |
True if plugged in via USB-C or on Qi dock. |
chamber_name |
str |
Detected chamber: "3D", "3DXL", "Proxy", or "None". |
operating_state |
Enum |
IDLE, HEAT_PREHEAT, HEAT_ACTIVE, HEAT_FADE. |
time_remaining |
int |
Remaining session seconds during active heating. |
total_time |
int |
Total programmed duration of active profile. |
lifetime_dabs |
int |
Total completed sessions stored in hardware memory. |
boost_temp_f |
int |
Configured heat boost temperature increment (°F). |
boost_duration_s |
int |
Configured heat boost session extension (seconds). |
Lorax VFS Protocol
Puffco firmware implements a sequence-multiplexed file system over BLE:
| GATT Entity | UUID | Properties |
|---|---|---|
| Service | e276967f-ea8a-478a-a92e-d78f5dd15dd5 |
Primary Lorax BLE Service |
| Command | 60133d5c-5727-4f2c-9697-d842c5292a3c |
Write Without Response |
| Reply | 8dc5ec05-8f7d-45ad-99db-3fbde65dbd9c |
Notify / Indicate |
VFS Paths
/p/app/stat/id— Operating state byte (0=Idle, 1=Preheat, 2=Sesh, 3=Fade)/p/app/htr/temp— Chamber temperature sensor reading/p/app/mc— Mode control (0x07=Start,0x08=Stop,0x09=Boost)/p/bat/soc— Battery state of charge percentage/p/app/odom/0/nc— Lifetime completed dabs counter
Authentication Flow
- GATT Connection — Bleak discovers device via Lorax service UUID and establishes an MTU-negotiated link.
- Seed Challenge — Client sends opcode
0x00(GET_ACCESS_SEED) to request a 16-byte random seed. - SHA-256 Auth — Client computes
SHA256(MasterKey + Seed)[:16]and sends opcode0x01(UNLOCK_ACCESS). - Stream Telemetry — Client polls VFS paths for live temperature, state, and battery level.
Platform Troubleshooting & Gotchas
1. The Single-Central Rule (Exclusive Connection)
Puffco hardware only supports one active BLE connection at a time. If the official Puffco iOS/Android app or Puffco Web App (Chrome) is running in the background on your phone, the device will reject connection attempts from puffco-py.
Solution: Completely force-close the mobile app and close any active Web Bluetooth browser tabs before running your Python scripts or CLI commands.
2. Linux & Raspberry Pi (BlueZ Permissions)
The Linux kernel restricts raw BLE socket creation to root by default. If device scanning returns zero results or throws PermissionDenied:
sudo setcap 'cap_net_raw,cap_net_admin+eip' $(readlink -f $(which python3))
sudo systemctl status bluetooth
3. macOS Bluetooth Permissions
On macOS Ventura, Sonoma, and Sequoia, terminal applications require explicit authorization under TCC privacy controls. Go to System Settings > Privacy & Security > Bluetooth and ensure your terminal app (Terminal, iTerm2, or VS Code) is toggled ON.
4. Windows 10 / 11 Bluetooth Pairing
The Windows WinRT BLE stack requires the Puffco Peak Pro to be paired in Windows Settings before third-party applications can enumerate GATT services. Open Settings > Bluetooth & devices > Add device, put your Peak Pro into pairing mode, and pair it once in Windows.
5. Running Offline Tests
pytest tests/ -v # or: python3 -m unittest discover tests/