Skip to main content

puffco-py 💨

PyPI Version Python Versions License: MIT Powered by Bleak

A standalone, modern, asynchronous Python client and real-time telemetry engine for Puffco Peak Pro (V1, V2, Limited Editions) and Puffco Proxy smart vaporizers over Bluetooth Low Energy (BLE).


✨ Features

  • Lorax VFS Protocol: Native support for modern Puffco firmware (Lorax Virtual File System) with asynchronous sequence packet multiplexing and SHA-256 challenge-response authentication.
  • ⏱️ Sub-100ms Low Latency: Adaptive polling engine capable of ~16 FPS streaming during active heating sessions (60ms) and low-power polling during idle (250ms).
  • 🎮 Full Session Control: Start sessions, abort/stop, trigger heat boosts, change active profile slots, set custom target temperatures, and toggle stealth mode.
  • 🧵 Thread-Safe & GUI Ready: Includes ThreadedPuffcoClient for effortless integration with synchronous scripts, desktop GUIs (Tkinter, PyQt), and macOS menu bar widgets (rumps).
  • 🔍 Auto-Discovery: Automatic scanning and filtering for nearby Puffco hardware based on manufacturer OUI prefixes and Lorax service UUIDs.
  • 🛠️ Built-in CLI: Terminal utilities (puffco-py scan, puffco-py monitor, puffco-py sesh) ready out of the box.

💡 Comparison & Advancements (vs. puffcoble)

We acknowledge the earlier exploratory work of Fr0st3h / PuffcoBLE (puffcoble), which demonstrated basic BLE reads. puffco-py was independently created from scratch to provide a modern, production-grade SDK and real-time telemetry engine:

  • Reactive Telemetry Engine: Rather than issuing manual one-shot reads, puffco-py continuously streams live bowl temperature, session countdowns, and battery state via an adaptive background loop with event-driven Observer listeners (add_telemetry_listener, add_state_listener).
  • Strongly-Typed Domain Models: Built on rich dataclasses (PuffcoTelemetry, PuffcoDeviceInfo) and enums (DeviceState, ChamberType) with automatic Fahrenheit/Celsius conversions, replacing raw untyped primitives.
  • Threaded GUI Bridge: Includes PuffcoThreadedClient running the event loop on an isolated daemon thread, making it seamless to integrate with Tkinter, PyQt, PySide, and macOS menu bar widgets without fighting asyncio event loop threading issues.
  • Interactive Terminal Suite: Built-in CLI with live terminal dashboards (puffco-py monitor) powered by rich, device discovery (puffco-py scan), diagnostics (puffco-py info), and session controls.
  • Broader Python Compatibility: Supports Python 3.9+ (compared to 3.11+).
  • Automated Test Suite: Full offline unit test suite with mock packet framing, sequence serialization, and SHA-256 challenge authentication verification.

📦 Installation

Install from source or local checkout:

git clone https://github.com/SoyMilkIsOk/puffco-py.git
cd puffco-py
pip install .

To include optional CLI and GUI dependencies:

pip install ".[cli,gui]"

🚀 Quickstart

1. Async Python (Core Client)

import asyncio
from puffco_py import PuffcoClient

async def main():
    # Automatically scans for and connects to the nearest Puffco device
    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}%")

        # Stream live telemetry updates
        def on_update(telemetry):
            print(f"\rTemp: {telemetry.live_temp_f:.1f}°F | State: {telemetry.state_name}", end="")

        client.add_telemetry_listener(on_update)
        await client.start_telemetry_stream()

        # Keep streaming for 15 seconds
        await asyncio.sleep(15)

        client.remove_telemetry_listener(on_update)

asyncio.run(main())

2. Synchronous & GUI Python (Threaded Client)

If you are building a desktop GUI (Tkinter, PyQt, PySide, wxPython) or a synchronous script, use ThreadedPuffcoClient. It executes all asynchronous BLE communications inside a dedicated background worker thread with its own event loop, exposing simple thread-safe methods to your main UI thread without blocking event dispatching:

import time
from puffco_py import ThreadedPuffcoClient

client = ThreadedPuffcoClient()

# Register real-time callbacks from the background thread
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())
    
    # Synchronous thread-safe controls from main UI thread
    client.set_profile(slot=1)
    client.start_session()
    time.sleep(10)
    client.boost()
    time.sleep(10)
    client.stop_session()

client.stop()

🖥️ Command-Line Interface (CLI)

The package installs a standalone terminal tool puffco-py:

# 1. Scan for nearby devices (5s timeout)
puffco-py scan

# 2. Stream a live terminal telemetry HUD (Ctrl+C to exit)
puffco-py monitor

# 3. View hardware serial, firmware version, lifetime dabs, and heat profiles
puffco-py info

# 4. Trigger or abort heat sessions
puffco-py sesh start
puffco-py sesh boost
puffco-py sesh stop

CLI Command Reference

Command Arguments Description
puffco-py scan -t, --timeout <sec> (default: 5.0) Scans BLE advertisements and prints discovered devices with MAC addresses and RSSI signal strength.
puffco-py monitor --mac <MAC/UUID> (optional) Live streams bowl temperature, target temp, battery %, chamber type, and session state.
puffco-py info --mac <MAC/UUID> (optional) Connects and dumps hardware serial, firmware version, lifetime dab count, stealth status, and all 4 profile slot configurations.
puffco-py sesh <action> start, stop, or boost
--mac <MAC/UUID> (optional)
Sends immediate heating commands to the connected device.

🍏 Native macOS Menu Bar Widget

A fully functional, native status bar widget is provided in examples/04_mac_menu_widget.py (powered by rumps).

┌────────────────────────────────────────────────────────┐
│  🔥 520°F (35s)       (or: 💨 480°F | 85% when idle)   │
└┬───────────────────────────────────────────────────────┘
 │  Status: Heating (Sesh Active)
 │  Battery: 85% (⚡ Charging)
 │  Chamber: 3DXL
 │  Total Dabs: 1,420
 │  ───────────────────────────
 │  🚀 Start Heat Sesh
 │  ⚡ Boost Heat (+15s / +10°F)
 │  🛑 Abort Sesh
 └─────────────────────────────

Running the Menu Bar App:

# Install GUI extra dependencies (rumps on macOS)
pip install ".[gui]"

# Run the widget
python3 examples/04_mac_menu_widget.py

📁 Examples & Guides

The examples/ folder contains self-contained reference scripts:

Example Execution Model Description
01_live_telemetry.py Async (asyncio) Minimal quickstart: connects and streams live bowl temperature & battery.
02_session_control.py Async (asyncio) Inspects profile slots, switches active profile, toggles stealth mode.
03_threaded_gui.py Threaded / Sync Background worker pattern for desktop GUIs (Tkinter, PyQt, PySide).
04_mac_menu_widget.py Desktop GUI (rumps) Full macOS menu bar status indicator and session remote control.

See examples/README.md for in-depth explanations, prerequisites, and troubleshooting tips.


📚 Python API Reference

Client Methods (PuffcoClient / ThreadedPuffcoClient)

Method Async / Sync Description
start_session() Both Initiates a heating session using the currently active profile.
stop_session() Both Aborts and cancels an active heating session immediately.
boost() Both Triggers heat boost during an active session (+15s / +10°F).
set_profile(slot) Both Changes active heat profile slot (integer 0 to 3).
set_stealth_mode(enabled) Both Toggles LED lights on (False) or off (True).
start_telemetry_stream(...) Async Starts continuous polling engine (60ms active sesh, 250ms idle).
add_telemetry_listener(fn) Both Registers a callback fn(telemetry) called upon state change.

Telemetry Model (PuffcoTelemetry)

Available via client.telemetry:

  • live_temp_f: Current real-time temperature of the heating bowl in °F.
  • target_temp_f: Target temperature of the active profile in °F.
  • battery_pct: Battery level (0100%).
  • is_charging: Boolean indicating USB-C or wireless Qi charging state.
  • chamber_name: Connected chamber type ("3D", "3DXL", "Proxy", or "None").
  • operating_state: Current state enum (IDLE, HEAT_PREHEAT, HEAT_ACTIVE, HEAT_FADE).
  • is_heating: Boolean flag that is True whenever the chamber is actively powered.
  • time_remaining: Remaining seconds in active heating cycle.
  • lifetime_dabs: Lifetime total dab counter (odometer).
  • profiles: List of 4 saved profile configurations (Profile(slot, name, temp_f, duration_s)).

🔬 How the Protocol Works

Puffco devices communicate over Bluetooth Low Energy (BLE) using the Lorax Virtual File System (VFS):

 central central (PC/Mac/RPi)                 Puffco Device
      │                                             │
      │──────── 1. Connect (Bleak GATT) ───────────>│
      │                                             │
      │<─────── 2. Lorax Version / Notify ──────────│
      │                                             │
      │── 3. GET_ACCESS_SEED (Opcode 0x00) ────────>│
      │<─ 16-Byte Random Seed Challenge ────────────│
      │                                             │
      │── 4. UNLOCK_ACCESS (Opcode 0x01) ──────────>│
      │      Token = SHA256(MasterKey + Seed)[:16]  │
      │<─ Status: SUCCESS (0x00) ───────────────────│
      │                                             │
      │── 5. READ_SHORT ("/p/app/htr/temp") ───────>│
      │<─ Live Chamber Temp (Float / Int Tenths) ───│
  1. Service UUID: e276967f-ea8a-478a-a92e-d78f5dd15dd5
  2. Command Characteristic: 60133d5c-5727-4f2c-9697-d842c5292a3c (Write Without Response)
  3. Reply Characteristic: 8dc5ec05-8f7d-45ad-99db-3fbde65dbd9c (Notify / Indicate)
  4. VFS Paths:
    • /p/app/stat/id: Operating state byte (Idle, Preheat, Active, Fade).
    • /p/app/htr/temp: Live bowl temperature.
    • /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 odometer dab count.

🧪 Running Tests

Run the offline unit test suite:

python3 -m unittest discover tests/

🙏 Credits & Acknowledgments

This library builds upon foundational protocol research and reverse-engineering from the community:

  • Fr0st3h / PuffcoBLE: Early Python library exploring Lorax path reads and LED lighting controls.
  • PuffcoPC by meekzyr: The original project exploring Puffco Bluetooth communication and pairing.
  • home-assistant-puffco: Fantastic Home Assistant integration documenting Lorax paths and characteristics.
  • Bleak: The backbone asynchronous BLE library for Python.

📜 License

This project is licensed under the MIT License.


puffco-py is an independent, community-developed open-source reverse-engineering project. It is NOT affiliated with, authorized, maintained, sponsored, or endorsed by Puff Corp. (registered in Delaware) or any of its affiliates. All trademarks—including Puffco, Peak, Peak Pro, Proxy, 3D Chamber, and 3DXL—are registered trademarks of Puff Corp. Nominative use of these names is strictly for identification and interoperability purposes under Fair Use.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

puffco_py-0.1.0.tar.gz (27.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

puffco_py-0.1.0-py3-none-any.whl (23.6 kB view details)

Uploaded Python 3

File details

Details for the file puffco_py-0.1.0.tar.gz.

File metadata

  • Download URL: puffco_py-0.1.0.tar.gz
  • Upload date:
  • Size: 27.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.11.4

File hashes

Hashes for puffco_py-0.1.0.tar.gz
Algorithm Hash digest
SHA256 362e839567439dffefca2a827b6fb708c44af08f7b4c072b146d9cc9c1cea4f3
MD5 f8136f1040c25f397046eeb6b1720d06
BLAKE2b-256 ff8ae82b470d78bfd5111f99c133b7e1c946ec88adea761b6159e1af538a9375

See more details on using hashes here.

File details

Details for the file puffco_py-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: puffco_py-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 23.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.11.4

File hashes

Hashes for puffco_py-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 abf6b20ccb2d732f01f14cf112f10fde0452dd5c5c0e713e7bc3c3f2611ea07d
MD5 95fd8c3e633a1138a3401a1ca9fc163a
BLAKE2b-256 2dd73f5b6b4355c5a4d804244d0efb2e078a62444ec61bff6e425ebdadf4ab83

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 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