Skip to main content

spacepak-modbus

Read and control SpacePak Solstice Inverter Extreme (ILAHP) air-to-water heat pumps over Modbus, as typed Python objects rather than register numbers.

The library maps the ILAHP register set onto modbus-connection's device model: you hand it a ModbusUnit, call async_update(), and read sub-systems as attributes. It owns no connection and no I/O policy. The caller does.

Status

This is a working reference pulled from one real installation, shared in case it is useful. It is not an actively maintained package: there is no commitment to triage issues or pull requests, or to track future modbus-connection changes. Forks are welcome, and so is anyone who wants to take it over.

Supported devices

The SpacePak Solstice Inverter Extreme (ILAHP) range, per the register map in its installation manual (ILHP2-0423). It has been exercised against two units behind a Modbus TCP gateway. Other SpacePak models are untested.

The unit speaks Modbus RTU at 9600 baud, 8N1, on its RS-485 port. Every register this library touches is a holding register (as opposed to an input register), whether it reads it or writes it. See Writing below for what it can change.

What it covers

Component Registers What
controls 1011-1012, 1018, 1021, 1023-1024, 1028-1030, 1158-1165 Power, operating mode, auto restart (H01), cooling enabled (H05), field-wired control (H07), Modbus address (H10), hot water function enabled, display unit (H21), silence mode, heating and cooling targets, the unit's own setpoint limits
tuning 1037-1039, 1053, 1160-1161, 1166-1169, 1174-1175, 1192-1193, 1197-1199, 1219-1220, 1228-1230, 1234-1236 Read-only installer parameters: antifreeze protection, restart and stop differentials, outlet overheat margin, low-ambient compensation, freeze-protection pump run, maximum water temperatures, shutdown and restart outdoor temperatures, pump mode, compressor frequency limits, weather compensation
status 2011-2034 Running, current mode (defrost included), the effective water targets after limits and weather compensation, load outputs, compressor hours, field switch inputs
measurements 2042-2072 Water, air, coil and refrigerant temperatures, currents, voltages, compressor frequency
faults 2081-2090 The nine failure registers, decoded into named faults

The manual lists room temperature (T09, 2058) and water flow (T39, 2077) as not used on this unit, so they are left out. The hot water tank temperature (T08) only has a sensor behind it when the unit's hot water function is enabled (controls.hot_water_enabled).

tuning is read-only. The rest of the installer parameters (EEV steps, defrost timing, fan setup, sterilize, timers) are left out. Writing installer parameters by mistake would misconfigure the heat pump, not just misreport it.

Every temperature is stored in tenths of a degree Celsius, whatever the unit's display is set to show, so every temperature here is in °C.

Usage

import asyncio

from modbus_connection import ModbusTcpParams
from modbus_connection.tmodbus import ModbusConnection
from spacepak_modbus import IlahpHeatPump


async def main() -> None:
    connection = ModbusConnection(ModbusTcpParams(host="192.168.1.50", port=502))
    try:
        heat_pump = IlahpHeatPump(connection.for_unit(1))
        await heat_pump.async_update()

        print("Running:", heat_pump.status.running, heat_pump.status.unit_mode)
        print("Outlet:", heat_pump.measurements.outlet_temperature, "°C")
        print("Outdoor:", heat_pump.measurements.ambient_temperature, "°C")
        print("Target:", heat_pump.controls.heating_target_temperature, "°C")
        print("Faults:", [fault.key for fault in heat_pump.faults.active_faults])
    finally:
        await connection.close()


asyncio.run(main())

Readings and settings refresh separately

  • async_update_readings() reads status, measurements and faults: what the unit measures and reports.
  • async_update_settings() reads controls and tuning: registers that change when something writes them. Run one after a write to read back what took effect.
  • async_update() does both, in one report.

Each returns an UpdateReport. A component whose block the unit refuses keeps its previous values and is listed in report.failed; the rest still refresh. A dead link (ModbusConnectionError) raises, and so does a timeout before anything has answered.

Writing

await heat_pump.controls.write("power_on", True)
await heat_pump.controls.write("heating_target_temperature", 45.0)
await heat_pump.async_update_settings()

Setpoint writes are checked against the widest range each register accepts. The unit applies its own tighter limits too, which it reports as min_heating_setpoint/max_heating_setpoint and min_cooling_setpoint/max_cooling_setpoint. Clamp to those before writing.

The operating mode is read-only here. The mode register's values depend on whether the installer enabled cooling (parameter H05), and a wrong mode can leave a hydronic system without heat.

Faults

The unit reports faults as nine bitmask registers. faults.active_faults decodes them into Fault entries, each with its failure register, bit, a stable snake_case key, and a description. FAULTS is the full table of the bits the manual names. A set bit the manual does not name (failure register 3 has no published bit table) is still reported, as failure_<n>_bit_<b>, so nothing is hidden. faults.any_fault is true when any register has a bit set.

Checking a real heat pump

script/query.py reads one unit once and prints every value, which is the quickest way to see whether a unit is reachable and addressed correctly:

uv run script/query.py 192.168.1.50 --port 502 --unit 1
uv run script/query.py socket://192.168.1.50:8899 --transport serial --unit 1
uv run script/query.py /dev/ttyUSB0 --transport serial --baudrate 9600 --unit 1 --raw

--raw adds every register it read, undecoded, which is what an issue about a wrong value should quote. A raw dump loads straight into modbus-connection's mock with load_raw(), so it can become a test.

Development

uv sync
uv run pytest
uv run ruff check . && uv run ruff format --check . && uv run mypy

The tests run against modbus-connection's in-memory mock backend. No hardware is needed.

Source

The register map comes from the SpacePak ILAHP Installation, Operation & Maintenance manual (ILHP2-0423), Modbus section. Addresses, scales and the enum and bit tables follow it. On live units:

  • 1011, 1158 and 1159 have been read and written.
  • Every register this library reads has been read from two units and checked against the values an existing integration reported for the same units.
  • The operating mode (1012) and the setpoint limits (1162-1165) read as expected. The limits are whatever the installer configured, so they differ from the manual's defaults.

License

Unlicense: public domain.

Metadata

Release files for spacepak-modbus 0.2.0

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

Source distribution (sdist)

Source distribution for spacepak-modbus 0.2.0
File Size Uploaded Provenance Actions
spacepak_modbus-0.2.0.tar.gz 46.5 kB With provenance Details for spacepak_modbus-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for spacepak-modbus 0.2.0
File Interpreter ABI Platform Actions
spacepak_modbus-0.2.0-py3-none-any.whl Python 3 none any Details for spacepak_modbus-0.2.0-py3-none-any.whl

Total release size: 62.5 kB

Release files / spacepak_modbus-0.2.0.tar.gz

Download URL spacepak_modbus-0.2.0.tar.gz
Size 46.5 kB
Tags Source
SHA-256 checksum
How to use checksums
fd31ba695d5c5a0cc4e99bd6d54bf6ad9e19c7aa6ef4a764cad73834bd27e81b
BLAKE2b-256 checksum
How to use checksums
f60c56943d58e1c2a8e23911732f08cb83def4d3c47f06f86f009f0e38c0f8c4
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 Oct 11, 2026.

Transparency log

Release files / spacepak_modbus-0.2.0-py3-none-any.whl

Download URL spacepak_modbus-0.2.0-py3-none-any.whl
Size 15.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8fba61b75d4d8e192328c11e7be5bccdd5ce45a34684eb47c55e00756c9bce5d
BLAKE2b-256 checksum
How to use checksums
b8eb9969f60aa98542fa60b420bbb82a92c4e79c447489c30dc53a56f0b11cf9
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 Oct 11, 2026.

Transparency log

Security for spacepak-modbus 0.2.0

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.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for spacepak_modbus-0.2.0.tar.gz

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 Oct 11, 2026.

Transparency log

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.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files Full provenance

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