Allow a climate entity to have an independent on / off state attribute

This commit is contained in:
jbouwh 2024-12-30 21:57:19 +00:00
parent a0fb6df5ba
commit e2d9ca9cd9
2 changed files with 58 additions and 1 deletions

View File

@ -240,6 +240,7 @@ CACHED_PROPERTIES_WITH_ATTR_ = {
"preset_mode",
"preset_modes",
"is_aux_heat",
"is_on",
"fan_mode",
"fan_modes",
"swing_mode",
@ -280,6 +281,7 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
_attr_hvac_mode: HVACMode | None
_attr_hvac_modes: list[HVACMode]
_attr_is_aux_heat: bool | None
_attr_is_on: bool | None
_attr_max_humidity: float = DEFAULT_MAX_HUMIDITY
_attr_max_temp: float
_attr_min_humidity: float = DEFAULT_MIN_HUMIDITY
@ -357,6 +359,26 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
return HVACMode(hvac_mode).value # type: ignore[unreachable]
return hvac_mode.value
@property
def is_on(self) -> bool | None:
"""Return True if the climate is turned on.
The climate's on/off state can be be controlled independently
from the hvac_action and hvac_mode if the _attr_is_on attribute is set.
If the _attr_is_on attrubiute is set, then return that value.
Otherwise, return True if hvac_action is not None and not HVACAction.OFF.
Return None if hvac_action is None,
otherwise return True if hvac_mode is not HVACMode.OFF.
"""
if hasattr(self, "_attr_is_on"):
return self._attr_is_on
if self.hvac_action is not None:
return self.hvac_action != HVACAction.OFF
if self.hvac_mode is None:
return None
return self.hvac_mode != HVACMode.OFF
@property
def precision(self) -> float:
"""Return the precision of the system."""

View File

@ -36,6 +36,7 @@ from homeassistant.components.climate.const import (
SWING_HORIZONTAL_OFF,
SWING_HORIZONTAL_ON,
ClimateEntityFeature,
HVACAction,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, UnitOfTemperature
@ -425,7 +426,7 @@ async def test_mode_validation(
async def test_turn_on_off_toggle(hass: HomeAssistant) -> None:
"""Test turn_on/turn_off/toggle methods."""
"""Test turn_on/turn_off/toggle methods and the is_on property."""
class MockClimateEntityTest(MockClimateEntity):
"""Mock Climate device."""
@ -440,20 +441,54 @@ async def test_turn_on_off_toggle(hass: HomeAssistant) -> None:
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set new target hvac mode."""
self._attr_hvac_mode = hvac_mode
self._attr_is_on = hvac_mode != HVACMode.OFF
climate = MockClimateEntityTest()
climate.hass = hass
assert climate.is_on is False
await climate.async_turn_on()
assert climate.hvac_mode == HVACMode.HEAT
assert climate.is_on is True
await climate.async_turn_off()
assert climate.hvac_mode == HVACMode.OFF
assert climate.is_on is False
await climate.async_toggle()
assert climate.hvac_mode == HVACMode.HEAT
assert climate.is_on is True
await climate.async_toggle()
assert climate.hvac_mode == HVACMode.OFF
assert climate.is_on is False
# Test is_on property in case _attr_is_on is not set
delattr(climate, "_attr_is_on")
# When hvac_action is set, is_on should depend on it
climate._attr_hvac_mode = HVACMode.AUTO
climate._attr_hvac_action = HVACAction.HEATING
assert climate.hvac_mode == HVACMode.AUTO
assert climate.hvac_action == HVACAction.HEATING
assert climate.is_on is True
climate._attr_hvac_action = HVACAction.OFF
assert climate.hvac_action == HVACAction.OFF
assert climate.is_on is False
# When hvac_action is not used and hvac_mode is set, is_on should depend on it
climate._attr_hvac_action = None
assert climate.hvac_action is None
assert climate.hvac_mode == HVACMode.AUTO
assert climate.is_on is True
climate._attr_hvac_mode = HVACMode.OFF
assert climate.hvac_mode == HVACMode.OFF
assert climate.is_on is False
# If no mode or action is set and _attr_is_on is not set, is_on should be None
climate._attr_hvac_action = None
climate._attr_hvac_mode = None
assert climate.is_on is None
async def test_sync_toggle(hass: HomeAssistant) -> None: