Skip to content

Commit

Permalink
Add presets to Advantage Air (#109485)
Browse files Browse the repository at this point in the history
* Add presets

* Make hvac_modes dynamic

* Add supported features

* Fix set preset mode

* Add coverage

* Remove unused constants

* Remove the extra newline

* Add snapshot assertion to new test

* Add comments

* Use ServiceValidationError

* Test for ServiceValidationError

* Fix typo

* Refactor to use _handle_coordinator_update

* Remove preset_mode prop

* Apply suggestions from code review

Co-authored-by: J. Nick Koston <nick@koston.org>

---------

Co-authored-by: J. Nick Koston <nick@koston.org>
  • Loading branch information
Bre77 and bdraco committed Feb 20, 2024
1 parent 9f8e4ce commit 5b00703
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 36 deletions.
114 changes: 81 additions & 33 deletions homeassistant/components/advantage_air/climate.py
Expand Up @@ -17,7 +17,8 @@
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .const import (
Expand Down Expand Up @@ -49,6 +50,24 @@
ADVANTAGE_AIR_COOL_TARGET = "myAutoCoolTargetTemp"
ADVANTAGE_AIR_MYFAN = "autoAA"

HVAC_MODES = [
HVACMode.OFF,
HVACMode.COOL,
HVACMode.HEAT,
HVACMode.FAN_ONLY,
HVACMode.DRY,
]
HVAC_MODES_MYAUTO = HVAC_MODES + [HVACMode.HEAT_COOL]
SUPPORTED_FEATURES = (
ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
SUPPORTED_FEATURES_MYZONE = SUPPORTED_FEATURES | ClimateEntityFeature.TARGET_TEMPERATURE
SUPPORTED_FEATURES_MYAUTO = (
SUPPORTED_FEATURES | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
)

PARALLEL_UPDATES = 0

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -84,34 +103,56 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity):
_attr_min_temp = 16
_attr_name = None
_enable_turn_on_off_backwards_compatibility = False
_support_preset = ClimateEntityFeature(0)

def __init__(self, instance: AdvantageAirData, ac_key: str) -> None:
"""Initialize an AdvantageAir AC unit."""
super().__init__(instance, ac_key)

self._attr_supported_features = (
ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
self._attr_hvac_modes = [
HVACMode.OFF,
HVACMode.COOL,
HVACMode.HEAT,
HVACMode.FAN_ONLY,
HVACMode.DRY,
]
# Set supported features and HVAC modes based on current operating mode
self._attr_preset_modes = [ADVANTAGE_AIR_MYZONE]

# Add "MyTemp" preset if available
if ADVANTAGE_AIR_MYTEMP_ENABLED in self._ac:
self._attr_preset_modes += [ADVANTAGE_AIR_MYTEMP]
self._support_preset = ClimateEntityFeature.PRESET_MODE

# Add "MyAuto" preset if available
if ADVANTAGE_AIR_MYAUTO_ENABLED in self._ac:
self._attr_preset_modes += [ADVANTAGE_AIR_MYAUTO]
self._support_preset = ClimateEntityFeature.PRESET_MODE

# Setup attributes based on current preset
self._async_configure_preset()

def _async_configure_preset(self) -> None:
"""Configure attributes based on preset."""

# Preset Changes
if self._ac.get(ADVANTAGE_AIR_MYAUTO_ENABLED):
# MyAuto
self._attr_supported_features |= (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
self._attr_preset_mode = ADVANTAGE_AIR_MYAUTO
self._attr_hvac_modes = HVAC_MODES_MYAUTO
self._attr_supported_features = (
SUPPORTED_FEATURES_MYAUTO | self._support_preset
)
self._attr_hvac_modes += [HVACMode.HEAT_COOL]
elif not self._ac.get(ADVANTAGE_AIR_MYTEMP_ENABLED):
elif self._ac.get(ADVANTAGE_AIR_MYTEMP_ENABLED):
# MyTemp
self._attr_preset_mode = ADVANTAGE_AIR_MYTEMP
self._attr_hvac_modes = HVAC_MODES
self._attr_supported_features = SUPPORTED_FEATURES | self._support_preset
else:
# MyZone
self._attr_supported_features |= ClimateEntityFeature.TARGET_TEMPERATURE
self._attr_preset_mode = ADVANTAGE_AIR_MYZONE
self._attr_hvac_modes = HVAC_MODES
self._attr_supported_features = (
SUPPORTED_FEATURES_MYZONE | self._support_preset
)

@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
self._async_configure_preset()
super()._handle_coordinator_update()

@property
def current_temperature(self) -> float | None:
Expand All @@ -124,11 +165,7 @@ def current_temperature(self) -> float | None:
def target_temperature(self) -> float | None:
"""Return the current target temperature."""
# If the system is in MyZone mode, and a zone is set, return that temperature instead.
if (
self._myzone
and not self._ac.get(ADVANTAGE_AIR_MYAUTO_ENABLED)
and not self._ac.get(ADVANTAGE_AIR_MYTEMP_ENABLED)
):
if self._myzone and self.preset_mode == ADVANTAGE_AIR_MYZONE:
return self._myzone["setTemp"]
return self._ac["setTemp"]

Expand Down Expand Up @@ -169,14 +206,15 @@ async def async_turn_off(self) -> None:
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set the HVAC Mode and State."""
if hvac_mode == HVACMode.OFF:
await self.async_update_ac({"state": ADVANTAGE_AIR_STATE_OFF})
else:
await self.async_update_ac(
{
"state": ADVANTAGE_AIR_STATE_ON,
"mode": HASS_HVAC_MODES.get(hvac_mode),
}
)
return await self.async_turn_off()
if hvac_mode == HVACMode.HEAT_COOL and self.preset_mode != ADVANTAGE_AIR_MYAUTO:
raise ServiceValidationError("Heat/Cool is not supported in this mode")
await self.async_update_ac(
{
"state": ADVANTAGE_AIR_STATE_ON,
"mode": HASS_HVAC_MODES.get(hvac_mode),
}
)

async def async_set_fan_mode(self, fan_mode: str) -> None:
"""Set the Fan Mode."""
Expand All @@ -198,6 +236,16 @@ async def async_set_temperature(self, **kwargs: Any) -> None:
}
)

async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set the preset mode."""
change = {}
if ADVANTAGE_AIR_MYTEMP_ENABLED in self._ac:
change[ADVANTAGE_AIR_MYTEMP_ENABLED] = preset_mode == ADVANTAGE_AIR_MYTEMP
if ADVANTAGE_AIR_MYAUTO_ENABLED in self._ac:
change[ADVANTAGE_AIR_MYAUTO_ENABLED] = preset_mode == ADVANTAGE_AIR_MYAUTO
if change:
await self.async_update_ac(change)


class AdvantageAirZone(AdvantageAirZoneEntity, ClimateEntity):
"""AdvantageAir MyTemp Zone control."""
Expand Down
19 changes: 17 additions & 2 deletions tests/components/advantage_air/snapshots/test_climate.ambr
Expand Up @@ -40,11 +40,16 @@
]),
'max_temp': 32,
'min_temp': 16,
'supported_features': <ClimateEntityFeature: 395>,
'preset_mode': 'MyAuto',
'preset_modes': list([
'MyZone',
'MyTemp',
'MyAuto',
]),
'supported_features': <ClimateEntityFeature: 410>,
'target_temp_high': 24,
'target_temp_low': 20,
'target_temp_step': 1,
'temperature': 24,
}),
'context': <ANY>,
'entity_id': 'climate.myauto',
Expand All @@ -53,3 +58,13 @@
'state': 'heat_cool',
})
# ---
# name: test_climate_myzone_main[climate.myzone-preset]
dict({
'ac1': dict({
'info': dict({
'climateControlModeEnabled': False,
'myAutoModeEnabled': True,
}),
}),
})
# ---
26 changes: 25 additions & 1 deletion tests/components/advantage_air/test_climate.py
Expand Up @@ -6,27 +6,30 @@
import pytest
from syrupy import SnapshotAssertion

from homeassistant.components.advantage_air.climate import ADVANTAGE_AIR_MYAUTO
from homeassistant.components.climate import (
ATTR_CURRENT_TEMPERATURE,
ATTR_FAN_MODE,
ATTR_HVAC_MODE,
ATTR_MAX_TEMP,
ATTR_MIN_TEMP,
ATTR_PRESET_MODE,
ATTR_TARGET_TEMP_HIGH,
ATTR_TARGET_TEMP_LOW,
DOMAIN as CLIMATE_DOMAIN,
FAN_AUTO,
FAN_LOW,
SERVICE_SET_FAN_MODE,
SERVICE_SET_HVAC_MODE,
SERVICE_SET_PRESET_MODE,
SERVICE_SET_TEMPERATURE,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
HVACMode,
)
from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import entity_registry as er

from . import add_mock_config
Expand All @@ -37,6 +40,7 @@ async def test_climate_myzone_main(
entity_registry: er.EntityRegistry,
mock_get: AsyncMock,
mock_update: AsyncMock,
snapshot: SnapshotAssertion,
) -> None:
"""Test climate platform main entity."""

Expand Down Expand Up @@ -125,6 +129,26 @@ async def test_climate_myzone_main(
mock_update.assert_called_once()
mock_update.reset_mock()

# Change Preset
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_PRESET_MODE,
{ATTR_ENTITY_ID: [entity_id], ATTR_PRESET_MODE: ADVANTAGE_AIR_MYAUTO},
blocking=True,
)
mock_update.assert_called_once()
assert mock_update.call_args[0][0] == snapshot(name=f"{entity_id}-preset")
mock_update.reset_mock()

# Test setting HEAT COOL when its not supported
with pytest.raises(ServiceValidationError):
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
{ATTR_ENTITY_ID: [entity_id], ATTR_HVAC_MODE: HVACMode.HEAT_COOL},
blocking=True,
)


async def test_climate_myzone_zone(
hass: HomeAssistant,
Expand Down

0 comments on commit 5b00703

Please sign in to comment.