Skip to main content

Logo

aiosolarfocus

Async Python client for Solarfocus ecomanager-touch heating systems, over Modbus TCP. The successor of pysolarfocus.

[!WARNING] Unofficial, and developed without support from Solarfocus. Writing to a heating system can damage the system or the building. Check with Solarfocus or your installer before changing anything you do not understand.

Install

pip install aiosolarfocus

Read

import asyncio

from aiosolarfocus import ApiVersion, SolarfocusClient, SolarfocusConfig, Systems


async def main() -> None:
    config = SolarfocusConfig(
        host="10.0.0.5",
        system=Systems.VAMPAIR,
        api_version=ApiVersion.V_26_020,
        heating_circuits=1,
        buffers=1,
        boilers=1,
    )
    async with SolarfocusClient(config) as client:
        result = await client.update()

        print(client.heat_pump.supply_temperature)  # float | None
        print(client.heating_circuits[0].mode)  # HeatingCircuitMode | None
        print(client.heat_pump.cop_heating)  # float | None

        if not result.ok:
            print("not read:", *result.failed)


asyncio.run(main())

Reading a value is not asynchronous — it is the last reading, already decoded. Only the calls that talk to the controller are awaited.

None means one of three things, and callers almost always want the same behaviour for all of them: this firmware or system does not have the register, it has not been read yet or its last read failed, or the channel is reporting one of the values an open sensor gives instead of a measurement. component.raw() and component.info() tell them apart.

Values the library works out from registers — the heat pump's coefficients of performance, its seasonal figures — are declared beside the registers they come from, so component.available_values() offers them alongside everything else and component.info() describes them. An idle heat pump reports None for a coefficient of performance rather than zero.

Write

from aiosolarfocus import HeatingCircuitMode

await client.heating_circuits[0].set_mode(HeatingCircuitMode.AUTOMATIC)
await client.heating_circuits[0].set_target_supply_temperature(45.0)
await client.photovoltaic.set_smart_meter(-1500)  # the library does the two's complement

# Registers the controller wants written together go out together.
await client.heating_circuits[0].set_operating_state(
    mode=HeatingCircuitMode.AUTOMATIC,
    target_supply_temperature=18.0,
)

A successful write updates the component's own cache, so the new value is readable straight away without re-reading anything.

Errors

update() raises only when the connection itself is the problem, because that is the one failure that says nothing about any particular component. Everything else — a range this firmware refuses, a controller answering an exception — is attributed to the components whose registers were in that read:

try:
    result = await client.update()
except SolarfocusConnectionError:
    ...  # the whole controller is unreachable
for key, error in result.failed.items():
    ...  # just these components went quiet

Without asyncio

from aiosolarfocus import SolarfocusConfig, SolarfocusSync

with SolarfocusSync(SolarfocusConfig(host="10.0.0.5")) as sync:
    sync.update()
    print(sync.client.heat_pump.supply_temperature)

Command line

python -m aiosolarfocus detect    --host 10.0.0.5 --evidence
python -m aiosolarfocus dump      --host 10.0.0.5
python -m aiosolarfocus watch     --host 10.0.0.5 --interval 10   # prints only what changed
python -m aiosolarfocus set       --host 10.0.0.5 heating_circuits.1.mode automatic

python -m aiosolarfocus registers --system Vampair --api-version 26.020   # no controller needed
python -m aiosolarfocus plan      --system Vampair --api-version 26.020   # no controller needed

Supported

Systems: vampair, therminator, ecotop, Pellet Elegance, octoplus. Firmware: 20.110 through 26.020. Give the version the controller prints on its own screen; a newer one clamps to the newest this library knows.

Only the vampair has been tested against real hardware. The other four are reasoned from the register specification — a register dump from one of them is the most useful thing you can contribute.

Documentation

Testing against it

aiosolarfocus.testing ships in the wheel, so a downstream test suite can drive a real client against a fake controller that reproduces the real one's behaviour — including refusing unmapped addresses and compacting reads that span a gap.

from aiosolarfocus.testing import FakeController

config = SolarfocusConfig(host="test")
client = SolarfocusClient(config, transport=FakeController.for_config(config))

About

Successor to pysolarfocus, rewritten async-native with the register map as validated data. Portions of this work derive from that project; see NOTICE.

Not affiliated with, endorsed by, or supported by Solarfocus GmbH.

Metadata

Release files for aiosolarfocus 0.2.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 aiosolarfocus 0.2.1
File Size Uploaded
aiosolarfocus-0.2.1.tar.gz 102.8 kB Details

Built distribution (wheel)

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

Total release size: 186.9 kB

Release files / aiosolarfocus-0.2.1.tar.gz

Download URL aiosolarfocus-0.2.1.tar.gz
Size 102.8 kB
Tags Source
SHA-256 checksum
How to use checksums
dc1f695cda765944437e75c9061767531e0ae1c26e797b514ecd3b33970b8b6c
BLAKE2b-256 checksum
How to use checksums
25599e14e063cb1e75e346616a4cb5ab095b7420481e3c6665e895ed0e7d4f6f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.6 {"installer":{"name":"uv","version":"0.12.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / aiosolarfocus-0.2.1-py3-none-any.whl

Download URL aiosolarfocus-0.2.1-py3-none-any.whl
Size 84.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7a71662b81f7f1f0e88f9c29c372cc946a2442ce56f4274c64cca4af5711c22a
BLAKE2b-256 checksum
How to use checksums
85dcb097c740ea7a338bb5a5467bdd4c093ad39f162ab24e67c1ff580e77add7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.6 {"installer":{"name":"uv","version":"0.12.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

0.3.0

2 release files

0.2.5

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

This release

0.2.1 This release

2 release files

0.2.0

2 release files

0.1.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