Add presets to Advantage Air (#109485)
* 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>pull/111042/head
parent
9f8e4cecdd
commit
5b00703ef7
|
@ -17,7 +17,8 @@ from homeassistant.components.climate import (
|
|||
)
|
||||
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 (
|
||||
|
@ -49,6 +50,24 @@ ADVANTAGE_AIR_HEAT_TARGET = "myAutoHeatTargetTemp"
|
|||
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__)
|
||||
|
@ -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:
|
||||
|
@ -124,11 +165,7 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity):
|
|||
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"]
|
||||
|
||||
|
@ -169,14 +206,15 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity):
|
|||
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."""
|
||||
|
@ -198,6 +236,16 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity):
|
|||
}
|
||||
)
|
||||
|
||||
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."""
|
||||
|
|
|
@ -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',
|
||||
|
@ -53,3 +58,13 @@
|
|||
'state': 'heat_cool',
|
||||
})
|
||||
# ---
|
||||
# name: test_climate_myzone_main[climate.myzone-preset]
|
||||
dict({
|
||||
'ac1': dict({
|
||||
'info': dict({
|
||||
'climateControlModeEnabled': False,
|
||||
'myAutoModeEnabled': True,
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
|
|
|
@ -6,12 +6,14 @@ from advantage_air import ApiError
|
|||
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,
|
||||
|
@ -19,6 +21,7 @@ from homeassistant.components.climate import (
|
|||
FAN_LOW,
|
||||
SERVICE_SET_FAN_MODE,
|
||||
SERVICE_SET_HVAC_MODE,
|
||||
SERVICE_SET_PRESET_MODE,
|
||||
SERVICE_SET_TEMPERATURE,
|
||||
SERVICE_TURN_OFF,
|
||||
SERVICE_TURN_ON,
|
||||
|
@ -26,7 +29,7 @@ from homeassistant.components.climate import (
|
|||
)
|
||||
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
|
||||
|
@ -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."""
|
||||
|
||||
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue