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)   │
└┬───────────────────────────────────────────────────────┘
 │  Device: SAMS PEAK
 │  Status: Ready
 │  Temp: 490.0°F (Target: 510°F)
 │  Battery: 85% (⚡ Charging)
 │  Chamber: 3DXL
 │  Total Dabs: 1,420
 │  ───────────────────────────
 │  🚀 Start Heat Sesh
 │  ⚡ Boost Heat (+15s / +10°F)
 │  🛑 Abort Sesh
 │  ───────────────────────────
 │  🎨 Heat Profiles     ▶  [Slot 1: Low 490°F (45s) ✓]
 │  ⚙️ Controls & UI     ▶  [Stealth Mode, Lantern Mode]
 │  📡 Devices & BLE     ▶  [🔍 Scan for Nearby Devices]
 │                          [✓ SAMS PEAK (-45 dBm)]
 │                          [✏️ Connect via MAC / UUID...]
 │                          [🔌 Disconnect Device]
 └─────────────────────────────

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 (0–100%).
  • 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.

Metadata

Release files for puffco-py 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for puffco-py 0.1.1
File Size Uploaded Provenance Actions
puffco_py-0.1.1.tar.gz 28.3 kB Without provenance Details for puffco_py-0.1.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for puffco-py 0.1.1
File Interpreter ABI Platform Actions
puffco_py-0.1.1-py3-none-any.whl Python 3 none any Details for puffco_py-0.1.1-py3-none-any.whl

Total release size: 52.8 kB

Release files / puffco_py-0.1.1.tar.gz

Download URL puffco_py-0.1.1.tar.gz
Size 28.3 kB
Tags Source
SHA-256 checksum
How to use checksums
9104bcaf0916e0669317104699a34786d29f0f254b586e2621e896447e185618
BLAKE2b-256 checksum
How to use checksums
44b4b2a9b3f1127098aa6aae4384078ca90ab61afaab65711e0ccc1919817b45
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.4

Release files / puffco_py-0.1.1-py3-none-any.whl

Download URL puffco_py-0.1.1-py3-none-any.whl
Size 24.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f2e5ab420e90bfcb351c20e6ab743210a3c312b44bb37bce03728dd7ddf6acbd
BLAKE2b-256 checksum
How to use checksums
c8d1f91ef536cda71dfe4864a33eee9be6f8e47e03af2c8d270732223d4d9378
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.4

Security for puffco-py 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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