Skip to content

Latest commit

 

History

90 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

python-duco-connectivity

Async Python client for the local Duco HTTP API.

python-duco-connectivity is a small async client for the unauthenticated local Duco HTTP endpoints that were validated during initial development. The library keeps its public models close to the API payload shape and is intended to stay reusable outside Home Assistant.

Installation

Until the first PyPI release is published, install directly from GitHub:

pip install git+https://github.com/ronaldvdmeer/python-duco-connectivity.git

After the package is published on PyPI, install it with:

pip install python-duco-connectivity

The package also installs a duco-probe CLI and supports module execution for quick function probes against a local Duco box. When you are not running inside an activated virtual environment, use the explicit .venv/bin/... paths shown in the development examples below.

Current scope

  • HTTP only
  • asynchronous communication via aiohttp
  • typed stable config families for the documented /config branches
  • typed helpers for stable /info fields such as heat recovery filter time
  • a typed overview for selected values from one unfiltered /info request
  • natural empty results for optional capability discovery and explicit errors for strict capability access
  • typed models that stay close to the API response shape
  • preserved raw_payload data on typed response models for forward compatibility

Error handling

Discovery-style helpers use their natural empty result when a Duco box reports an optional capability as unsupported: filter time returns None, ventilation temperatures return an empty VentilationTemperatureInfo, and bulk bypass targets return {}. Malformed responses and operational failures remain exceptions. The strict parameter-specific bypass helper raises DucoUnsupportedCapabilityError for an unsupported target; this DucoResponseError subclass preserves the HTTP status, path, and response body. The bypass-target helpers only return models with complete and coherent value, minimum, increment, and maximum metadata. Bulk reads omit an invalid individual zone without suppressing valid zones. Parameter-specific reads and write responses raise DucoError when the requested target is missing, incomplete, or inconsistent. BypassSupplyTemperatureTarget.validate_value() checks a Celsius value against the target-specific range and step, while normalize_value() rounds converted values to the nearest supported step. Pass the target to async_set_bypass_supply_temperature_target(..., target=target) to validate a write without an additional read.

Diagnostic subsystem reads expose known status values as normalized DiagStatus members (ok, disabled, or error). Each DiagComponent also keeps the exact API value in raw_status; an unrecognized future value produces status=None without discarding the raw value. Subsystem names remain unfiltered so future components are available to downstream consumers. Direct construction using a pre-0.13 raw status string remains compatible and derives raw_status automatically; callers using a normalized DiagStatus must provide raw_status explicitly.

async_get_info_overview() combines RSSI, diagnostic subsystems, heat recovery filter time, and ventilation temperatures from one GET /info request. Missing product-specific modules produce their natural empty values. Unlike most typed models, InfoOverview does not retain the broad response or the LAN raw payload, because that response can contain network credentials such as the Wi-Fi access point key.

Getting started

import asyncio

import aiohttp

from duco_connectivity import DucoClient


async def main() -> None:
    async with aiohttp.ClientSession() as session:
        client = DucoClient(session, "192.168.1.10")
        api_info = await client.async_get_api_info()
        info = await client.async_get_info_overview()
        nodes = await client.async_get_nodes_overview()

        print(api_info.public_api_version)
        print(info.rssi_wifi)
        print([node.node_id for node in nodes])


if __name__ == "__main__":
    asyncio.run(main())

Documentation map

Start with docs/api-reference.md when you want a compact inventory of the public client methods, exports, compatibility aliases, and construction rules.

  • docs/api-reference.md for the central public API inventory
  • docs/cli.md for the function probe CLI and shell examples
  • docs/config.md for system, node, and zone config reads and writes
  • docs/endpoint-inventory.md for the Public API 2.5 read routes and observed response shapes across tested Duco products
  • docs/live-testing.md for local opt-in tests against a real Duco device
  • docs/replay-testing.md for local sample validation against ignored raw API captures
  • docs/actions.md for action discovery and execution
  • docs/nodes.md for node models and node information readers
  • docs/public-api-boundaries.md for the typed-model contract and raw escape hatch boundaries
  • docs/zones.md for zone and group info and config readers
  • docs/ventilation-states.md for ventilation enum values and compatibility members
  • docs/payload-preservation.md for raw payload preservation and raw endpoint access

The public surface keeps a deliberate split between stable typed readers and broader raw escape hatches. Use the typed methods when the model already matches the data you need, and use the raw helpers when you need endpoint coverage, selector flexibility, or payload fields that have not been typed yet. See docs/public-api-boundaries.md for the full contract.

Testing strategy

The repository uses three automated test layers:

  • Synthetic unit tests cover focused parser and client behavior with mocked HTTP responses.
  • Local sample-validation tests can replay a small set of typed client methods against your own ignored raw API captures.
  • Live tests validate read paths, safe writes, and latency probes against your own Duco device.

That split matters for Duco support. Synthetic tests keep day-to-day iteration fast. Local sample validation lets you check real captures without committing them or maintaining a sanitization workflow. Live tests confirm that the client still behaves correctly against actual hardware.

Public API maintenance

The compact API reference is generated from the published exports and public async client methods. Regenerate it after public surface changes with:

python tools/api_reference.py write

Development

From the repository root, use any activated virtual environment you prefer. The commands below use a local .venv so they stay copy-pasteable from a clean checkout. Create it first if needed, then install the development dependencies and run the same checks as CI:

python -m venv .venv
.venv/bin/python -m pip install -e ".[dev]"
.venv/bin/pytest
.venv/bin/ruff check src tests
.venv/bin/ruff format --check src tests
.venv/bin/mypy src
.venv/bin/bandit -r src -ll
.venv/bin/pip-audit --desc on

For local function probes without activating the environment first:

.venv/bin/python -m duco_connectivity --host 192.168.1.10 call async_get_board_info
.venv/bin/duco-probe --host 192.168.1.10 call async_get_board_info
.venv/bin/duco-probe --host 192.168.1.10 call async_get_ventilation_temperature_info
.venv/bin/duco-probe --host 192.168.1.10 call async_get_bypass_supply_temperature_targets
.venv/bin/duco-probe --host 192.168.1.10 call async_get_bypass_supply_temperature_target --kwargs '{"zone_id": 1}'

For local real-device validation against your own Duco box, use the opt-in workflow documented in docs/live-testing.md.

For local sample validation against ignored raw captures, use docs/replay-testing.md.

If you want to validate raw API captures locally, follow the layout guidance in docs/replay-testing.md and the fixture-specific notes in tests/fixtures/replay/README.md.

Validation

The current API surface was validated against a real Duco box during the first development pass, covering:

  • GET /api
  • GET /info with generic module, submodule, and parameter queries
  • GET /config with generic module, submodule, and parameter queries
  • PATCH /config with a no-op TimeZone write against the current value
  • GET /info?module=General&submodule=Board
  • GET /info?module=General&submodule=Lan
  • GET /info?module=Ventilation
  • GET /info?module=HeatRecovery
  • GET /info/nodes
  • GET /info?module=General&submodule=PublicApi
  • GET /config?module=HeatRecovery&submodule=Bypass&parameter=TempSupTgtZone1
  • PATCH /config?module=HeatRecovery&submodule=Bypass&parameter=TempSupTgtZone1 with a no-op write against the current value
  • POST /action/nodes/{node} with a no-op SetVentilationState

The repository now also includes opt-in local live tests so the same read and safe-write checks can be repeated against your own device without changing the default mock-only test workflow.

About

This is a small async client for the unauthenticated local Duco HTTP endpoints that were validated during initial development. The library keeps its public models close to the API payload shape and is intended to stay reusable outside Home Assistant.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Contributors

Languages