This release is a pre-release and may not be stable for production use.
pytriplesolar
Asynchronous Python library for the Triple Solar PVT Heat Pump 5.0, over Modbus/TLS.
It is built on modbus-connection:
the library describes the heat pump's registers as typed components and takes a
ModbusUnit. It never opens a connection itself, so the application decides
how and when to connect, and can share one connection between users.
Requirements
- Python 3.13+
- A Triple Solar heat pump with Modbus TCP set to secure (the default), on the same network. Discovery uses mDNS, which does not cross routers or VLANs.
Pairing
Modbus/TLS uses mutual certificate authentication. A client pairs once to get a certificate signed by the heat pump's own root CA:
- Enable pairing mode in the My Triple Solar app. The heat pump then runs a
single-use signing service for one minute, announced over mDNS-SD as
_ts-csr._tcp. - Browse for that service with your zeroconf library of choice. Then pass its
address, port and host name to
async_pair:
import aiohttp
from pytriplesolar import async_pair
async with aiohttp.ClientSession() as session:
credentials = await async_pair(
session, "192.0.2.10", 6509, device_hostname="hpcb-a1b2c3.local"
)
Credentials holds the root CA, the client certificate and key (PEM), plus the
host name the heat pump was paired under. Store it with as_dict() and restore
it with Credentials.from_dict(). It contains a private key, so keep it
private.
Reading and writing
Find the paired heat pump's _mbap-s._tcp service (port 802), then connect
with the credentials:
from pathlib import Path
from modbus_connection.tmodbus import connect_tls
from pytriplesolar import Profile, SpaceConditioningMode, TripleSolarHeatPump
files = credentials.write_files(Path("certs")) # blocking: ssl needs files
params = files.tls_params("192.0.2.10")
connection = await connect_tls(
params.host,
port=params.port,
verify=params.verify,
check_hostname=params.check_hostname,
client_cert=params.client_cert,
client_key=params.client_key,
)
heat_pump = TripleSolarHeatPump(connection.for_unit(1))
report = await heat_pump.async_update()
print(heat_pump.general.state, [str(a) for a in heat_pump.alerts.active])
print(heat_pump.dhw.tank_temperature, heat_pump.heat_pump_module_1.electric_power)
await heat_pump.settings.write("dhw_profile", Profile.COMFORT)
await heat_pump.dhw_comfort.write("tank_temperature_setpoint", 55.0)
await heat_pump.control.write("mode", SpaceConditioningMode.HEATING)
The server certificate is verified against the heat pump's root CA. That CA is unique to the unit, so the connection is pinned to the paired heat pump. The host name is not checked, because the heat pump is normally reached by IP.
Sub-systems
| Attribute | Registers | Contents |
|---|---|---|
general |
input | overall state, total rated power |
alerts |
input | up to 20 active alerts, decoded to service codes (F10203) |
dhw |
input | DHW state, demand, modulation, tank temperature and setpoint, valve, active profile |
space_heating / space_cooling |
input | state, demand, modulation, sink supply temperature and setpoint |
space_conditioning_input |
input | room/ambient temperature, humidity, room setpoint, thermostat modulation |
heat_pump_module_1 / _2 |
input | module state, compressor frequency, pumps, cooling valve, source/sink temperatures, electric/thermal power, compressor starts |
settings |
holding (R/W) | space heating / cooling / DHW enabled and profile |
dhw_eco / dhw_normal / dhw_comfort |
holding (R/W) | tank setpoint (10–70 °C), lower hysteresis (0–50 °C), modulation (20–100 %) per DHW profile |
control |
holding (R/W) | external control: mode, modulation level (0–100 %), power budget (W) |
heat_pump_module_2 is None on a unit with a single module (detected on the
first update).
Values the heat pump does not currently have (for example, no room thermostat
connected) read as None. The heat pump refuses a whole block of registers as
soon as one value in it is missing, so the library then re-reads that
sub-system one register at a time.
External control
The control registers live in memory only. The heat pump acts on them only
while a Modbus client has been active within the last 30 seconds
(CONTROL_KEEPALIVE_SECONDS). Poll faster than that while controlling it.
Development
python -m venv .venv
.venv/bin/pip install -e ".[test]"
.venv/bin/pytest
Tests run against modbus-connection's in-memory mock unit via its pytest
plugin (mock_modbus_unit fixture), so no hardware is needed.
License
Apache-2.0
Metadata
Release files for pytriplesolar 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 | Provenance | Actions |
|---|---|---|---|---|
| pytriplesolar-0.1.0a1.tar.gz | 28.1 kB | With provenance | Details for pytriplesolar-0.1.0a1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| pytriplesolar-0.1.0a1-py3-none-any.whl | Python 3 | none | any | Details for pytriplesolar-0.1.0a1-py3-none-any.whl |
Total release size: 50.7 kB
Release files / pytriplesolar-0.1.0a1.tar.gz
| Download URL | pytriplesolar-0.1.0a1.tar.gz |
|---|---|
| Size | 28.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
154d17d40129bad350281dbb4ed6cf29d98b2c848c0efc465603e91cdb38d68e
|
|
BLAKE2b-256 checksum How to use checksums |
a3a52f23be90bbaed8ec255f9ee9b4ad043168652850d6d5d6c208fb5bc22e59
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Nov 7, 2026.
Transparency logRelease files / pytriplesolar-0.1.0a1-py3-none-any.whl
| Download URL | pytriplesolar-0.1.0a1-py3-none-any.whl |
|---|---|
| Size | 22.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
cfcbb8c98176ad2fd092d45a2da920d8cc0ac75b0c55faa6218250b1a383e8d2
|
|
BLAKE2b-256 checksum How to use checksums |
30c3255f3508f8e9ec39d7f370cd60bdade58f7215317ebff2484a8e2b41e6bd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Nov 7, 2026.
Transparency logSecurity for pytriplesolar 0.1.0a1
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.
Attestation for pytriplesolar-0.1.0a1.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 Nov 7, 2026.
Transparency logA 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.