Skip to content

Add experimental Zonal Control support for Duco valves #21

Description

@ronaldvdmeer

Summary

Add the remaining experimental zonal control support to the Duco Home Assistant integration for valve-style nodes after the VLV sensor work.

The remaining scope is:

  • a discrete control surface for ventilation mode changes on supported zonal nodes
  • optional read-only valve-style reporting for zone opening
  • any narrowly scoped follow-up sensor work that is still missing after the current VLV sensor PR

Why this issue exists

The python-duco-connectivity library already exposes the relevant node families and data:

  • node types like VLV, VLVCO2, VLVRH, VLVCO2RH, and EAV
  • Node.ventilation.state
  • Node.ventilation.flow_lvl_tgt
  • Node.motor_state.pos

The current Home Assistant integration already has a static FanEntity for the BOX node and dynamic sensor handling for non-BOX nodes.

The VLV sensor expansion is already covered separately by PR 171182. This issue should track the remaining zonal entity work instead of duplicating that PR.

Already in progress elsewhere

PR 171182 covers the initial sensor expansion for:

  • VLVCO2
  • VLVRH
  • VLVCO2RH

That PR extends the existing sensor mappings for CO2 and humidity values and adds focused tests.

Do not duplicate that work in this issue unless follow-up changes are needed after review.

Remaining implementation shape

1. Select for actual zone control

Use a SelectEntity as the writable control surface for supported zonal nodes.

Rationale:

  • Duco control is discrete (AUTO, MAN1, MAN2, MAN3, CNT1, CNT2, CNT3, EMPT), not a true generic percentage interface.
  • The integration already uses async_set_ventilation_state() for the box fan entity, so the same wrapper should be reused instead of calling a more generic node action directly.

Recommended mapping:

  • current_option <- node.ventilation.state
  • options = the intentionally supported base states exposed by the integration
  • async_select_option() -> client.async_set_ventilation_state(node_id, state) followed by coordinator refresh

Important detail:
The library enum also includes timed manual variants such as MAN1x2 and MAN1x3.
If those can appear on zonal nodes, the select entity should normalize them back to MAN1 / MAN2 / MAN3 for display instead of exposing internal timing variants as user-facing options.

2. Optional read-only valve entity

A ValveEntity is defensible only as a reporting entity, not as the write API.

Recommended first-pass behavior if implemented:

  • current_valve_position from node.ventilation.flow_lvl_tgt
  • reports_position = True
  • is_closed = None
  • is_closing = None
  • is_opening = None
  • no supported write features

Do not implement set_valve_position in this batch.

Rationale:

  • Duco write semantics are not a generic percentage contract.
  • Mapping Home Assistant percentage writes to installer-defined Duco states would be misleading.
  • The authoritative write path should remain the discrete ventilation-state select.

Open design question to validate before coding:

  • whether flow_lvl_tgt is the best user-facing proxy for valve openness, or whether motor_state.pos is more representative on real devices

If sample payloads are unavailable, prefer flow_lvl_tgt for the first experimental version because it is already normalized and closer to the intended airflow target.

3. Any follow-up sensor work should be narrow

After PR 171182, only keep sensor work here if there is a concrete remaining gap, for example:

  • EAVCO2 / EAVRH / EAV variants confirmed by real payloads
  • a review-driven correction to the VLV sensor mapping

Do not keep generic sensor expansion here if it is already handled by the existing PR.

Integration constraints

The current core integration structure matters for this work:

  • sensor.py already handles dynamically appearing and disappearing nodes via a coordinator listener.
  • fan.py does not do dynamic node handling because it only targets the static BOX node.
  • Any new select or valve platform for zonal nodes should follow the dynamic node pattern from sensor.py, not the static setup pattern from fan.py.

Scope

In scope:

  • add Platform.SELECT
  • possibly add Platform.VALVE
  • add a new select.py for discrete zone control
  • add a new read-only valve.py only if the proxy field choice is accepted
  • add translations for any new select or valve entities
  • add tests for dynamic add/remove behavior of new zonal nodes
  • add tests for select state normalization if timed manual variants are possible
  • add only narrow follow-up sensor work that remains after PR 171182

Out of scope:

  • duplicating the VLV sensor work already covered by PR 171182
  • direct SetPosMan or SetPosManCnt support
  • generic config editing for zone devices
  • large refactors of the existing Duco integration structure
  • library-side protocol changes unless a real integration blocker is discovered

Acceptance criteria

  • A zonal SelectEntity exists for supported valve-style nodes and uses the existing async_set_ventilation_state() write path.
  • New non-BOX node platforms handle dynamic node appearance and disappearance correctly.
  • If a valve entity is included, it is read-only and does not expose percentage write controls.
  • Tests cover steady-state entities, dynamic node add/remove, and write behavior for the select platform.
  • Any remaining sensor additions are limited to gaps that are not already covered by PR 171182.

Related files

Likely files to touch:

  • homeassistant/components/duco/const.py
  • homeassistant/components/duco/select.py
  • homeassistant/components/duco/valve.py
  • homeassistant/components/duco/strings.json
  • tests/components/duco/conftest.py
  • tests/components/duco/test_select.py
  • tests/components/duco/test_valve.py

Possible follow-up files only if needed:

  • homeassistant/components/duco/sensor.py
  • tests/components/duco/test_sensor.py

Related

  • VLV sensor support is being handled in PR 171182.
  • Related library capability is already present in python-duco-connectivity.
  • This is core integration work and belongs on Project 5, not the library board.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    Status
    In progress

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions