From 60d4b050ac13b1a2ee25f0e323b0db28e1fe3f77 Mon Sep 17 00:00:00 2001 From: David Recordon Date: Wed, 18 Feb 2026 07:35:57 -0800 Subject: [PATCH] Fix Control4 HVAC action mapping for multi-stage and idle states (#163222) --- homeassistant/components/control4/climate.py | 11 +++++++++-- tests/components/control4/test_climate.py | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/control4/climate.py b/homeassistant/components/control4/climate.py index 8669d09122d..ba0005cbf3a 100644 --- a/homeassistant/components/control4/climate.py +++ b/homeassistant/components/control4/climate.py @@ -75,11 +75,12 @@ C4_TO_HA_HVAC_MODE = { HA_TO_C4_HVAC_MODE = {v: k for k, v in C4_TO_HA_HVAC_MODE.items()} -# Map the five known Control4 HVAC states to Home Assistant HVAC actions +# Map Control4 HVAC states to Home Assistant HVAC actions C4_TO_HA_HVAC_ACTION = { "off": HVACAction.OFF, "heat": HVACAction.HEATING, "cool": HVACAction.COOLING, + "idle": HVACAction.IDLE, "dry": HVACAction.DRYING, "fan": HVACAction.FAN, } @@ -292,8 +293,14 @@ class Control4Climate(Control4Entity, ClimateEntity): c4_state = data.get(CONTROL4_HVAC_STATE) if c4_state is None: return None - # Convert state to lowercase for mapping action = C4_TO_HA_HVAC_ACTION.get(str(c4_state).lower()) + # Substring match for multi-stage systems that report + # e.g. "Stage 1 Heat", "Stage 2 Cool" + if action is None: + if "heat" in str(c4_state).lower(): + action = HVACAction.HEATING + elif "cool" in str(c4_state).lower(): + action = HVACAction.COOLING if action is None: _LOGGER.debug("Unknown HVAC state received from Control4: %s", c4_state) return action diff --git a/tests/components/control4/test_climate.py b/tests/components/control4/test_climate.py index 50015672e65..c77ebee1f65 100644 --- a/tests/components/control4/test_climate.py +++ b/tests/components/control4/test_climate.py @@ -115,6 +115,21 @@ async def test_climate_entities( HVACAction.FAN, id="fan", ), + pytest.param( + _make_climate_data(hvac_state="Idle"), + HVACAction.IDLE, + id="idle", + ), + pytest.param( + _make_climate_data(hvac_state="Stage 1 Heat"), + HVACAction.HEATING, + id="stage_1_heat", + ), + pytest.param( + _make_climate_data(hvac_state="Stage 2 Cool", hvac_mode="Cool"), + HVACAction.COOLING, + id="stage_2_cool", + ), ], ) @pytest.mark.usefixtures(