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
87 changes: 65 additions & 22 deletions homeassistant/components/advantage_air/climate.py
Expand Up @@ -18,6 +18,7 @@
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .const import (
Expand Down Expand Up @@ -89,6 +90,7 @@ def __init__(self, instance: AdvantageAirData, ac_key: str) -> None:
"""Initialize an AdvantageAir AC unit."""
super().__init__(instance, ac_key)

self._attr_preset_modes = [ADVANTAGE_AIR_MYZONE]
Bre77 marked this conversation as resolved.
Show resolved Hide resolved
self._attr_supported_features = (
ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.TURN_OFF
Expand All @@ -101,17 +103,42 @@ def __init__(self, instance: AdvantageAirData, ac_key: str) -> None:
HVACMode.FAN_ONLY,
HVACMode.DRY,
]
# Set supported features and HVAC modes based on current operating mode
if self._ac.get(ADVANTAGE_AIR_MYAUTO_ENABLED):
# MyAuto
self._attr_supported_features |= (
ClimateEntityFeature.TARGET_TEMPERATURE

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

# Add "MyAuto" preset if available
if ADVANTAGE_AIR_MYAUTO_ENABLED in self._ac:
self._attr_preset_modes += [ADVANTAGE_AIR_MYAUTO]
self._attr_supported_features |= ClimateEntityFeature.PRESET_MODE
Bre77 marked this conversation as resolved.
Show resolved Hide resolved

@property
def hvac_modes(self) -> list[HVACMode]:
Bre77 marked this conversation as resolved.
Show resolved Hide resolved
"""Return the list of available HVAC modes."""
# Dynamically adjust the supported HVAC modes based on the preset mode
if self.preset_mode == ADVANTAGE_AIR_MYAUTO:
return self._attr_hvac_modes + [HVACMode.HEAT_COOL]
return self._attr_hvac_modes

@property
def supported_features(self) -> ClimateEntityFeature:
Bre77 marked this conversation as resolved.
Show resolved Hide resolved
"""Return the supported features."""
# Dynamically adjust the supported features based on the preset mode
if self.preset_mode == ADVANTAGE_AIR_MYZONE:
# MyZone supports a single target temp
return (
self._attr_supported_features | ClimateEntityFeature.TARGET_TEMPERATURE
)
if self.preset_mode == ADVANTAGE_AIR_MYAUTO:
# MyAuto supports a target temp range
return (
self._attr_supported_features
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
)
self._attr_hvac_modes += [HVACMode.HEAT_COOL]
elif not self._ac.get(ADVANTAGE_AIR_MYTEMP_ENABLED):
# MyZone
self._attr_supported_features |= ClimateEntityFeature.TARGET_TEMPERATURE
# MyTemp does not support any master temperature control
return self._attr_supported_features

@property
def current_temperature(self) -> float | None:
Expand All @@ -124,11 +151,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 @@ -166,17 +189,27 @@ async def async_turn_off(self) -> None:
}
)

@property
def preset_mode(self) -> str:
"""Return the current preset mode."""
if self._ac.get(ADVANTAGE_AIR_MYAUTO_ENABLED):
return ADVANTAGE_AIR_MYAUTO
if self._ac.get(ADVANTAGE_AIR_MYTEMP_ENABLED):
return ADVANTAGE_AIR_MYTEMP
return ADVANTAGE_AIR_MYZONE

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 +231,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