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:
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.
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:
Why this issue exists
The
python-duco-connectivitylibrary already exposes the relevant node families and data:VLV,VLVCO2,VLVRH,VLVCO2RH, andEAVNode.ventilation.stateNode.ventilation.flow_lvl_tgtNode.motor_state.posThe current Home Assistant integration already has a static
FanEntityfor theBOXnode 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:
VLVCO2VLVRHVLVCO2RHThat 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
SelectEntityas the writable control surface for supported zonal nodes.Rationale:
AUTO,MAN1,MAN2,MAN3,CNT1,CNT2,CNT3,EMPT), not a true generic percentage interface.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.stateoptions= the intentionally supported base states exposed by the integrationasync_select_option()->client.async_set_ventilation_state(node_id, state)followed by coordinator refreshImportant detail:
The library enum also includes timed manual variants such as
MAN1x2andMAN1x3.If those can appear on zonal nodes, the select entity should normalize them back to
MAN1/MAN2/MAN3for display instead of exposing internal timing variants as user-facing options.2. Optional read-only valve entity
A
ValveEntityis defensible only as a reporting entity, not as the write API.Recommended first-pass behavior if implemented:
current_valve_positionfromnode.ventilation.flow_lvl_tgtreports_position = Trueis_closed = Noneis_closing = Noneis_opening = NoneDo not implement
set_valve_positionin this batch.Rationale:
Open design question to validate before coding:
flow_lvl_tgtis the best user-facing proxy for valve openness, or whethermotor_state.posis more representative on real devicesIf sample payloads are unavailable, prefer
flow_lvl_tgtfor 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/EAVvariants confirmed by real payloadsDo 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.pyalready handles dynamically appearing and disappearing nodes via a coordinator listener.fan.pydoes not do dynamic node handling because it only targets the staticBOXnode.selectorvalveplatform for zonal nodes should follow the dynamic node pattern fromsensor.py, not the static setup pattern fromfan.py.Scope
In scope:
Platform.SELECTPlatform.VALVEselect.pyfor discrete zone controlvalve.pyonly if the proxy field choice is acceptedOut of scope:
SetPosManorSetPosManCntsupportAcceptance criteria
SelectEntityexists for supported valve-style nodes and uses the existingasync_set_ventilation_state()write path.Related files
Likely files to touch:
homeassistant/components/duco/const.pyhomeassistant/components/duco/select.pyhomeassistant/components/duco/valve.pyhomeassistant/components/duco/strings.jsontests/components/duco/conftest.pytests/components/duco/test_select.pytests/components/duco/test_valve.pyPossible follow-up files only if needed:
homeassistant/components/duco/sensor.pytests/components/duco/test_sensor.pyRelated
python-duco-connectivity.