Skip to main content

steamloop

CI Status Documentation Status Test coverage percentage

uv Ruff pre-commit

PyPI Version Supported Python versions License


Async Python library for local control of thermostat devices over mTLS (port 7878).

Installation

pip install steamloop

CLI

Pairing

Put the thermostat in pairing mode (Menu > Settings > Network > Advanced Setup > Remote Connection > Pair), then:

steamloop 192.168.1.100 --pair

This saves a pairing file in the current directory with the secret key.

Monitoring

steamloop 192.168.1.100

If already paired, you can pass the secret key directly to skip the pairing file:

steamloop 192.168.1.100 --key YOUR_SECRET_KEY

Interactive commands: status, heat <temp>, cool <temp>, mode <off|auto|cool|heat>, fan <auto|on|circulate>, eheat <on|off>, help.

Library Usage

import asyncio
from steamloop import ThermostatConnection, ZoneMode, FanMode

async def main():
    conn = ThermostatConnection(
        "192.168.1.100",
        secret_key="your-secret-key-from-pairing",
    )
    async with conn:
        # State is populated automatically from thermostat events
        for zone_id, zone in conn.state.zones.items():
            print(f"{zone.name}: {zone.indoor_temperature}°F")

        # Send commands (sync — no await needed)
        conn.set_temperature_setpoint("1", heat_setpoint="72")
        conn.set_zone_mode("1", ZoneMode.COOL)
        conn.set_fan_mode(FanMode.AUTO)

asyncio.run(main())

Pairing Programmatically

pair() returns the secret key directly — store it however you like:

from steamloop import ThermostatConnection

async def pair(ip: str) -> str:
    conn = ThermostatConnection(ip, secret_key="")
    try:
        await conn.connect()
        ssk = await conn.pair()
        return ssk["secret_key"]  # store in a database, config entry, etc.
    finally:
        await conn.disconnect()

Or use the built-in file helpers to save/load pairing data to disk:

from steamloop import ThermostatConnection, save_pairing, load_pairing

# Save after pairing
await save_pairing(ip, {
    "secret_key": secret_key,
    "device_type": "automation",
    "device_id": "module",
})

# Load later
pairing = await load_pairing(ip)
conn = ThermostatConnection(ip, secret_key=pairing["secret_key"])

Event Callbacks

def on_event(msg):
    print("Received:", msg)

remove = conn.add_event_callback(on_event)
# later: remove() to unregister

Home Assistant Integration

Key design points for using steamloop in a Home Assistant integration:

  • Commands are sync — set_zone_mode(), set_fan_mode(), set_temperature_setpoint() use transport.write() internally, so they won't block the event loop. No await needed.
  • State is always fresh — the asyncio.Protocol receives events via data_received() and updates conn.state automatically. Just read properties directly.
  • Auto-reconnect — after calling start_background_tasks(), the connection automatically reconnects with exponential backoff (5s, 10s, 20s, ... up to 5 min).
  • Event callbacks — use add_event_callback() to trigger async_write_ha_state() when the thermostat pushes updates.
  • Multi-zone — create one ClimateEntity per conn.state.zones entry. Zones are populated automatically after login.

API Reference

ThermostatConnection(ip, port=7878, *, secret_key, cert_set=None, device_type="automation", device_id="module")

Method Async Description
connect() yes Establish mTLS connection
login() yes Authenticate with secret key
pair() yes Pair and receive secret key
start_background_tasks() no Start heartbeat + auto-reconnect
disconnect() yes Close connection and stop tasks
set_temperature_setpoint(zone_id, *, heat_setpoint, cool_setpoint, hold_type) no Set zone temperature
set_zone_mode(zone_id, mode) no Set zone HVAC mode
set_fan_mode(mode) no Set fan mode
set_emergency_heat(enabled) no Toggle emergency heat
add_event_callback(fn) no Register event listener (returns unregister callable)

Supports async with for automatic connect/login/disconnect:

async with ThermostatConnection(ip, secret_key=key) as conn:
    ...  # connected, logged in, background tasks running
# automatically disconnected

Enums

  • ZoneMode — OFF, AUTO, COOL, HEAT
  • FanMode — AUTO, ALWAYS_ON, CIRCULATE
  • HoldType — UNDEFINED, MANUAL, SCHEDULE, HOLD

State

  • conn.state.zones — dict[str, Zone] with temperature, setpoints, mode per zone
  • conn.state.fan_mode — current FanMode
  • conn.state.supported_modes — list[ZoneMode]
  • conn.state.emergency_heat / relative_humidity / cooling_active / heating_active

Contributors

Thanks goes to these wonderful people (emoji key):

This project follows the all-contributors specification. Contributions of any kind welcome!

Credits

Copier

This package was created with Copier and the browniebroke/pypackage-template project template.

Metadata

Release files for steamloop 1.2.3

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

Source distribution (sdist)

Source distribution for steamloop 1.2.3
File Size Uploaded
steamloop-1.2.3.tar.gz 43.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for steamloop 1.2.3
File Interpreter ABI Platform
steamloop-1.2.3-py3-none-any.whl Python 3 none any Details

Total release size: 72.6 kB

Release files / steamloop-1.2.3.tar.gz

Download URL steamloop-1.2.3.tar.gz
Size 43.0 kB
Tags Source
SHA-256 checksum
How to use checksums
9d2f9ac36fe31281e650494399861538c6bc73fb0f8ddc07b276c74af632bb95
BLAKE2b-256 checksum
How to use checksums
c2e34d8ddcba2f7ac36c63b73e688b68594be668151a388348463beadbee3eb3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 Jun 22, 2026.

Transparency log

Release files / steamloop-1.2.3-py3-none-any.whl

Download URL steamloop-1.2.3-py3-none-any.whl
Size 29.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
548e459577c51fd48587ca5e86c9a613ca89b433538289e0580234be2f610568
BLAKE2b-256 checksum
How to use checksums
6146d346c719cdd8d41ebfd3fb1a953c9b8b8eef04b789bd8cfb1cc9f925dd83
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 Jun 22, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.2.3 This release

2 release files

1.2.2

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.0

2 release 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