Skip to main content

Logo

aiosolarfocus

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

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

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.4
File Size Uploaded
aiosolarfocus-0.2.4.tar.gz 106.6 kB Details

Built distribution (wheel)

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

Total release size: 192.4 kB

Release files / aiosolarfocus-0.2.4.tar.gz

Download URL aiosolarfocus-0.2.4.tar.gz
Size 106.6 kB
Tags Source
SHA-256 checksum
How to use checksums
e8b8146806a0f134576e96ef61308e9c039c91c1c23f2348d05a5879cf37e6f3
BLAKE2b-256 checksum
How to use checksums
fc7c9a98242f2ad3a7b1d1c14171a381e98df11dba1d822387e55f47fd8a0713
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.7 {"installer":{"name":"uv","version":"0.12.7","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.4-py3-none-any.whl

Download URL aiosolarfocus-0.2.4-py3-none-any.whl
Size 85.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
30f3013a5d76037f0bc1283a651fc889e2cd9d2b66c1d5b4d02c18cdee8bbd9b
BLAKE2b-256 checksum
How to use checksums
d833524fabff2f3c0b439dcff8edc006ebbe6f690465656e808ca725b32ed90d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.7 {"installer":{"name":"uv","version":"0.12.7","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

This release

0.2.4 This release

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

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