Add HomeKit thermostat fan state mapping for preheating, defrosting (#145353)

Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
jz-v 2025-05-23 02:57:01 +10:00 committed by GitHub
parent 4ee9fdc9fb
commit d8e0be69d1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 97 additions and 0 deletions

View File

@ -167,6 +167,8 @@ HC_HASS_TO_HOMEKIT_FAN_STATE = {
HVACAction.COOLING: FAN_STATE_ACTIVE,
HVACAction.DRYING: FAN_STATE_ACTIVE,
HVACAction.FAN: FAN_STATE_ACTIVE,
HVACAction.PREHEATING: FAN_STATE_IDLE,
HVACAction.DEFROSTING: FAN_STATE_IDLE,
}
HEAT_COOL_DEADBAND = 5

View File

@ -56,6 +56,9 @@ from homeassistant.components.homekit.const import (
PROP_MIN_VALUE,
)
from homeassistant.components.homekit.type_thermostats import (
FAN_STATE_ACTIVE,
FAN_STATE_IDLE,
FAN_STATE_INACTIVE,
HC_HEAT_COOL_AUTO,
HC_HEAT_COOL_COOL,
HC_HEAT_COOL_HEAT,
@ -2493,6 +2496,98 @@ async def test_thermostat_with_supported_features_target_temp_but_fan_mode_set(
assert not acc.fan_chars
async def test_thermostat_fan_state_with_preheating_and_defrosting(
hass: HomeAssistant, hk_driver
) -> None:
"""Test thermostat fan state mappings for preheating and defrosting actions."""
entity_id = "climate.test"
hass.states.async_set(
entity_id,
HVACMode.HEAT,
{
ATTR_SUPPORTED_FEATURES: ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.FAN_MODE,
ATTR_FAN_MODES: [FAN_AUTO, FAN_LOW, FAN_HIGH],
ATTR_HVAC_ACTION: HVACAction.IDLE,
ATTR_FAN_MODE: FAN_AUTO,
ATTR_HVAC_MODES: [HVACMode.HEAT, HVACMode.COOL, HVACMode.OFF],
},
)
await hass.async_block_till_done()
acc = Thermostat(hass, hk_driver, "Climate", entity_id, 1, None)
hk_driver.add_accessory(acc)
acc.run()
await hass.async_block_till_done()
# Verify fan state characteristics are available
assert CHAR_CURRENT_FAN_STATE in acc.fan_chars
assert hasattr(acc, "char_current_fan_state")
# Test PREHEATING action maps to FAN_STATE_IDLE
hass.states.async_set(
entity_id,
HVACMode.HEAT,
{
ATTR_SUPPORTED_FEATURES: ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.FAN_MODE,
ATTR_FAN_MODES: [FAN_AUTO, FAN_LOW, FAN_HIGH],
ATTR_HVAC_ACTION: HVACAction.PREHEATING,
ATTR_FAN_MODE: FAN_AUTO,
ATTR_HVAC_MODES: [HVACMode.HEAT, HVACMode.COOL, HVACMode.OFF],
},
)
await hass.async_block_till_done()
assert acc.char_current_fan_state.value == FAN_STATE_IDLE
# Test DEFROSTING action maps to FAN_STATE_IDLE
hass.states.async_set(
entity_id,
HVACMode.HEAT,
{
ATTR_SUPPORTED_FEATURES: ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.FAN_MODE,
ATTR_FAN_MODES: [FAN_AUTO, FAN_LOW, FAN_HIGH],
ATTR_HVAC_ACTION: HVACAction.DEFROSTING,
ATTR_FAN_MODE: FAN_AUTO,
ATTR_HVAC_MODES: [HVACMode.HEAT, HVACMode.COOL, HVACMode.OFF],
},
)
await hass.async_block_till_done()
assert acc.char_current_fan_state.value == FAN_STATE_IDLE
# Test other actions for comparison
hass.states.async_set(
entity_id,
HVACMode.HEAT,
{
ATTR_SUPPORTED_FEATURES: ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.FAN_MODE,
ATTR_FAN_MODES: [FAN_AUTO, FAN_LOW, FAN_HIGH],
ATTR_HVAC_ACTION: HVACAction.HEATING,
ATTR_FAN_MODE: FAN_AUTO,
ATTR_HVAC_MODES: [HVACMode.HEAT, HVACMode.COOL, HVACMode.OFF],
},
)
await hass.async_block_till_done()
assert acc.char_current_fan_state.value == FAN_STATE_ACTIVE
hass.states.async_set(
entity_id,
HVACMode.OFF,
{
ATTR_SUPPORTED_FEATURES: ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.FAN_MODE,
ATTR_FAN_MODES: [FAN_AUTO, FAN_LOW, FAN_HIGH],
ATTR_HVAC_ACTION: HVACAction.OFF,
ATTR_FAN_MODE: FAN_AUTO,
ATTR_HVAC_MODES: [HVACMode.HEAT, HVACMode.COOL, HVACMode.OFF],
},
)
await hass.async_block_till_done()
assert acc.char_current_fan_state.value == FAN_STATE_INACTIVE
async def test_thermostat_handles_unknown_state(hass: HomeAssistant, hk_driver) -> None:
"""Test a thermostat can handle unknown state."""
entity_id = "climate.test"