This release is a pre-release and may not be stable for production use.
joule-ble
Asynchronous Python library for interfacing with ChefSteps and Breville Joule sous vide devices over Bluetooth Low Energy (BLE).
Designed following Home Assistant standards for Bluetooth integration libraries, supporting both passive advertisement discovery and active appliance control.
Features
- Hardware Generation Support:
- Original ChefSteps Joule (
CS10001/CS20001): ChefSteps Stream GATT Service (700b4321-9836-4383-a2b2-31a9098d1473). - Breville Joule Turbo (
CS30001): Breville FreeRTOS GATT Service (c6f2d9e3-49e7-4125-9014-bfc6d669ff00).
- Original ChefSteps Joule (
- Complete Sous Vide Operations:
- Start cook program (setpoint temperature in °C, optional cook time, delayed start, holding temperature).
- Adjust target temperature setpoint dynamically during cooking.
- Stop heating and circulation.
- Clear errors and faults.
- Identify device (flashes indicator LED).
- Authenticated session pairing and key exchange support.
- Rich Live Telemetry:
- Bath water temperature (°C).
- Heater temperature (°C).
- Upper and lower control board temperatures (°C).
- Motor RPM and motor current / PWM.
- Program step (Idle, Preheating, Waiting for food, Cooking, Complete, Error).
- Low water level and safety fault detection.
- Home Assistant Standards:
- Modern
bleakandbleak-retry-connectorconnection management with auto-reconnect handling. - Built-in
JouleBluetoothDeviceDatafor BLE advertisement decoding and device discovery in config flows. - Fully typed dataclasses with native Celsius float representation.
- Modern
Installation
pip install joule-ble
Quickstart
1. Advertisement Discovery (Config Flow)
from bleak.backends.scanner import AdvertisementData
from bleak.backends.device import BLEDevice
from joule_ble import JouleBluetoothDeviceData
scanner_parser = JouleBluetoothDeviceData()
# Called from Home Assistant bluetooth scanner callback:
device_info = scanner_parser.update(ble_device, advertisement_data)
if device_info:
print(f"Discovered {device_info.name} ({device_info.model}) at {device_info.mac_address}")
2. Controlling the Joule & Live Telemetry
import asyncio
from bleak import BleakScanner
from joule_ble import JouleClient, JouleState, MIN_TEMP_C, MAX_TEMP_C
async def main():
# Discover device
device = await BleakScanner.find_device_by_filter(
lambda d, adv: "joule" in (adv.local_name or "").lower()
)
if not device:
print("No Joule found!")
return
client = JouleClient(device)
# Listen for live status updates
def state_updated(state: JouleState):
print(
f"Bath: {state.bath_temp_c}°C | Target: {state.target_temp_c}°C | "
f"Step: {state.program_step} | RPM: {state.motor_rpm} | Low Water: {state.low_water}"
)
client.register_callback(state_updated)
await client.connect()
print("Connected! Device:", client.device_info)
# Start heating to 54.0°C with a 1 hour timer (3600s)
await client.start_cook(target_temp_c=54.0, cook_time_seconds=3600)
await asyncio.sleep(10)
# Change temperature setpoint to 55.0°C
await client.set_temperature(55.0)
await asyncio.sleep(10)
# Stop cooking
await client.stop_cook()
await client.disconnect()
asyncio.run(main())
Architecture & Protocols
The Joule uses Google Protocol Buffers (StreamMessage) transmitted across BLE GATT characteristics:
- ChefSteps Stream Service:
700b4321-9836-4383-a2b2-31a9098d1473- Write:
700b4322-9836-4383-a2b2-31a9098d1473 - Subscribe:
700b4325-9836-4383-a2b2-31a9098d1473
- Write:
- Breville Service:
c6f2d9e3-49e7-4125-9014-bfc6d669ff00- App Message (Write):
c6f2d9e3-49e7-4125-9014-bfc6d669ff09 - Appliance Message (Notify):
c6f2d9e3-49e7-4125-9014-bfc6d669ff08
- App Message (Write):
Messages exceeding the negotiated ATT MTU are automatically fragmented into chunks on transmission and reassembled upon reception.
License
Apache 2.0
Metadata
Release files for joule-ble 0.1.0a1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| joule_ble-0.1.0a1.tar.gz | 45.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| joule_ble-0.1.0a1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 91.5 kB
Release files / joule_ble-0.1.0a1.tar.gz
| Download URL | joule_ble-0.1.0a1.tar.gz |
|---|---|
| Size | 45.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
496a4bc19350ac055940b37139af121a2464b2dd12612aaecca5e8fadabc3eec
|
|
BLAKE2b-256 checksum How to use checksums |
db3baf9aa63d6ff3efb0e2736fdd444dcc7799808aab0ea3f36e7d9631cbfdca
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 13, 2026.
Transparency logRelease files / joule_ble-0.1.0a1-py3-none-any.whl
| Download URL | joule_ble-0.1.0a1-py3-none-any.whl |
|---|---|
| Size | 46.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4b6dde33afcdccd1ec09a4f1b3eca1bed865d45d42862b19b73d94592b7ff3d6
|
|
BLAKE2b-256 checksum How to use checksums |
16770cec01208035a48dd8239aa07b268657ede07cc5bd84ad83e4279f13783b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 13, 2026.
Transparency log