diff --git a/homeassistant/components/advantage_air/climate.py b/homeassistant/components/advantage_air/climate.py index 870a001a10f..6abd0b18fd4 100644 --- a/homeassistant/components/advantage_air/climate.py +++ b/homeassistant/components/advantage_air/climate.py @@ -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.""" diff --git a/tests/components/advantage_air/snapshots/test_climate.ambr b/tests/components/advantage_air/snapshots/test_climate.ambr index 28addf01ecd..6b8c18e7c87 100644 --- a/tests/components/advantage_air/snapshots/test_climate.ambr +++ b/tests/components/advantage_air/snapshots/test_climate.ambr @@ -40,11 +40,16 @@ ]), 'max_temp': 32, 'min_temp': 16, - 'supported_features': , + 'preset_mode': 'MyAuto', + 'preset_modes': list([ + 'MyZone', + 'MyTemp', + 'MyAuto', + ]), + 'supported_features': , 'target_temp_high': 24, 'target_temp_low': 20, 'target_temp_step': 1, - 'temperature': 24, }), 'context': , '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, + }), + }), + }) +# --- diff --git a/tests/components/advantage_air/test_climate.py b/tests/components/advantage_air/test_climate.py index 704e25c0473..66f8f869ae1 100644 --- a/tests/components/advantage_air/test_climate.py +++ b/tests/components/advantage_air/test_climate.py @@ -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,