Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add presets to Advantage Air #109485

Merged
merged 16 commits into from Feb 20, 2024
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]
Bre77 marked this conversation as resolved.
Show resolved Hide resolved

# 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.configure_preset()

def configure_preset(self) -> None:
Bre77 marked this conversation as resolved.
Show resolved Hide resolved
"""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.configure_preset()
Bre77 marked this conversation as resolved.
Show resolved Hide resolved
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")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a follow-up PR I think adding translations would be nice 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I also want to use _handle_coordinator_update for more platforms and values

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