diff --git a/homeassistant/components/advantage_air/climate.py b/homeassistant/components/advantage_air/climate.py index 8244472f2b4b..a488ba8b362e 100644 --- a/homeassistant/components/advantage_air/climate.py +++ b/homeassistant/components/advantage_air/climate.py @@ -21,6 +21,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import ( + ADVANTAGE_AIR_AUTOFAN_ENABLED, ADVANTAGE_AIR_STATE_CLOSE, ADVANTAGE_AIR_STATE_OFF, ADVANTAGE_AIR_STATE_ON, @@ -39,16 +40,6 @@ ADVANTAGE_AIR_HVAC_MODES = { } HASS_HVAC_MODES = {v: k for k, v in ADVANTAGE_AIR_HVAC_MODES.items()} -ADVANTAGE_AIR_FAN_MODES = { - "autoAA": FAN_AUTO, - "low": FAN_LOW, - "medium": FAN_MEDIUM, - "high": FAN_HIGH, -} -HASS_FAN_MODES = {v: k for k, v in ADVANTAGE_AIR_FAN_MODES.items()} -FAN_SPEEDS = {FAN_LOW: 30, FAN_MEDIUM: 60, FAN_HIGH: 100} - -ADVANTAGE_AIR_AUTOFAN = "aaAutoFanModeEnabled" ADVANTAGE_AIR_MYZONE = "MyZone" ADVANTAGE_AIR_MYAUTO = "MyAuto" ADVANTAGE_AIR_MYAUTO_ENABLED = "myAutoModeEnabled" @@ -56,6 +47,7 @@ ADVANTAGE_AIR_MYTEMP = "MyTemp" ADVANTAGE_AIR_MYTEMP_ENABLED = "climateControlModeEnabled" ADVANTAGE_AIR_HEAT_TARGET = "myAutoHeatTargetTemp" ADVANTAGE_AIR_COOL_TARGET = "myAutoCoolTargetTemp" +ADVANTAGE_AIR_MYFAN = "autoAA" PARALLEL_UPDATES = 0 @@ -85,27 +77,25 @@ async def async_setup_entry( class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity): """AdvantageAir AC unit.""" - _attr_fan_modes = [FAN_LOW, FAN_MEDIUM, FAN_HIGH] + _attr_fan_modes = [FAN_LOW, FAN_MEDIUM, FAN_HIGH, FAN_AUTO] _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_target_temperature_step = PRECISION_WHOLE _attr_max_temp = 32 _attr_min_temp = 16 _attr_name = None - _attr_hvac_modes = [ - HVACMode.OFF, - HVACMode.COOL, - HVACMode.HEAT, - HVACMode.FAN_ONLY, - HVACMode.DRY, - ] - - _attr_supported_features = ClimateEntityFeature.FAN_MODE - 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 + 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 if self._ac.get(ADVANTAGE_AIR_MYAUTO_ENABLED): # MyAuto @@ -118,10 +108,6 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity): # MyZone self._attr_supported_features |= ClimateEntityFeature.TARGET_TEMPERATURE - # Add "ezfan" mode if supported - if self._ac.get(ADVANTAGE_AIR_AUTOFAN): - self._attr_fan_modes += [FAN_AUTO] - @property def current_temperature(self) -> float | None: """Return the selected zones current temperature.""" @@ -151,7 +137,7 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity): @property def fan_mode(self) -> str | None: """Return the current fan modes.""" - return ADVANTAGE_AIR_FAN_MODES.get(self._ac["fan"]) + return FAN_AUTO if self._ac["fan"] == ADVANTAGE_AIR_MYFAN else self._ac["fan"] @property def target_temperature_high(self) -> float | None: @@ -189,7 +175,11 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity): async def async_set_fan_mode(self, fan_mode: str) -> None: """Set the Fan Mode.""" - await self.async_update_ac({"fan": HASS_FAN_MODES.get(fan_mode)}) + if fan_mode == FAN_AUTO and self._ac.get(ADVANTAGE_AIR_AUTOFAN_ENABLED): + mode = ADVANTAGE_AIR_MYFAN + else: + mode = fan_mode + await self.async_update_ac({"fan": mode}) async def async_set_temperature(self, **kwargs: Any) -> None: """Set the Temperature.""" diff --git a/homeassistant/components/advantage_air/const.py b/homeassistant/components/advantage_air/const.py index 5c044481ca05..80ce9b6eaa17 100644 --- a/homeassistant/components/advantage_air/const.py +++ b/homeassistant/components/advantage_air/const.py @@ -5,3 +5,4 @@ ADVANTAGE_AIR_STATE_OPEN = "open" ADVANTAGE_AIR_STATE_CLOSE = "close" ADVANTAGE_AIR_STATE_ON = "on" ADVANTAGE_AIR_STATE_OFF = "off" +ADVANTAGE_AIR_AUTOFAN_ENABLED = "aaAutoFanModeEnabled" diff --git a/homeassistant/components/advantage_air/switch.py b/homeassistant/components/advantage_air/switch.py index 7234ca363058..abc9b795d430 100644 --- a/homeassistant/components/advantage_air/switch.py +++ b/homeassistant/components/advantage_air/switch.py @@ -7,6 +7,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import ( + ADVANTAGE_AIR_AUTOFAN_ENABLED, ADVANTAGE_AIR_STATE_OFF, ADVANTAGE_AIR_STATE_ON, DOMAIN as ADVANTAGE_AIR_DOMAIN, @@ -29,6 +30,8 @@ async def async_setup_entry( for ac_key, ac_device in aircons.items(): if ac_device["info"]["freshAirStatus"] != "none": entities.append(AdvantageAirFreshAir(instance, ac_key)) + if ADVANTAGE_AIR_AUTOFAN_ENABLED in ac_device["info"]: + entities.append(AdvantageAirMyFan(instance, ac_key)) if things := instance.coordinator.data.get("myThings"): for thing in things["things"].values(): if thing["channelDipState"] == 8: # 8 = Other relay @@ -62,6 +65,32 @@ class AdvantageAirFreshAir(AdvantageAirAcEntity, SwitchEntity): await self.async_update_ac({"freshAirStatus": ADVANTAGE_AIR_STATE_OFF}) +class AdvantageAirMyFan(AdvantageAirAcEntity, SwitchEntity): + """Representation of Advantage Air MyFan control.""" + + _attr_icon = "mdi:fan-auto" + _attr_name = "MyFan" + _attr_device_class = SwitchDeviceClass.SWITCH + + def __init__(self, instance: AdvantageAirData, ac_key: str) -> None: + """Initialize an Advantage Air MyFan control.""" + super().__init__(instance, ac_key) + self._attr_unique_id += "-myfan" + + @property + def is_on(self) -> bool: + """Return the MyFan status.""" + return self._ac[ADVANTAGE_AIR_AUTOFAN_ENABLED] + + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn MyFan on.""" + await self.async_update_ac({ADVANTAGE_AIR_AUTOFAN_ENABLED: True}) + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn MyFan off.""" + await self.async_update_ac({ADVANTAGE_AIR_AUTOFAN_ENABLED: False}) + + class AdvantageAirRelay(AdvantageAirThingEntity, SwitchEntity): """Representation of Advantage Air Thing.""" diff --git a/tests/components/advantage_air/snapshots/test_climate.ambr b/tests/components/advantage_air/snapshots/test_climate.ambr new file mode 100644 index 000000000000..9e21d0ede177 --- /dev/null +++ b/tests/components/advantage_air/snapshots/test_climate.ambr @@ -0,0 +1,55 @@ +# serializer version: 1 +# name: test_climate_myauto_main[climate.myauto-fanmode] + dict({ + 'ac3': dict({ + 'info': dict({ + 'fan': 'autoAA', + }), + }), + }) +# --- +# name: test_climate_myauto_main[climate.myauto-settemp] + dict({ + 'ac3': dict({ + 'info': dict({ + 'myAutoCoolTargetTemp': 23.0, + 'myAutoHeatTargetTemp': 21.0, + }), + }), + }) +# --- +# name: test_climate_myauto_main[climate.myauto] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'current_temperature': None, + 'fan_mode': 'auto', + 'fan_modes': list([ + 'low', + 'medium', + 'high', + 'auto', + ]), + 'friendly_name': 'myauto', + 'hvac_modes': list([ + , + , + , + , + , + , + ]), + 'max_temp': 32, + 'min_temp': 16, + 'supported_features': , + 'target_temp_high': 24, + 'target_temp_low': 20, + 'target_temp_step': 1, + 'temperature': 24, + }), + 'context': , + 'entity_id': 'climate.myauto', + 'last_changed': , + 'last_updated': , + 'state': 'heat_cool', + }) +# --- diff --git a/tests/components/advantage_air/snapshots/test_switch.ambr b/tests/components/advantage_air/snapshots/test_switch.ambr new file mode 100644 index 000000000000..2060c0798ed3 --- /dev/null +++ b/tests/components/advantage_air/snapshots/test_switch.ambr @@ -0,0 +1,33 @@ +# serializer version: 1 +# name: test_cover_async_setup_entry[switch.myzone_myfan-turnoff] + dict({ + 'ac1': dict({ + 'info': dict({ + 'aaAutoFanModeEnabled': False, + }), + }), + }) +# --- +# name: test_cover_async_setup_entry[switch.myzone_myfan-turnon] + dict({ + 'ac1': dict({ + 'info': dict({ + 'aaAutoFanModeEnabled': True, + }), + }), + }) +# --- +# name: test_cover_async_setup_entry[switch.myzone_myfan] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'switch', + 'friendly_name': 'myzone MyFan', + 'icon': 'mdi:fan-auto', + }), + 'context': , + 'entity_id': 'switch.myzone_myfan', + 'last_changed': , + 'last_updated': , + 'state': 'off', + }) +# --- diff --git a/tests/components/advantage_air/test_climate.py b/tests/components/advantage_air/test_climate.py index ba97644501f5..704e25c04730 100644 --- a/tests/components/advantage_air/test_climate.py +++ b/tests/components/advantage_air/test_climate.py @@ -4,6 +4,7 @@ from unittest.mock import AsyncMock from advantage_air import ApiError import pytest +from syrupy import SnapshotAssertion from homeassistant.components.climate import ( ATTR_CURRENT_TEMPERATURE, @@ -14,6 +15,7 @@ from homeassistant.components.climate import ( ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW, DOMAIN as CLIMATE_DOMAIN, + FAN_AUTO, FAN_LOW, SERVICE_SET_FAN_MODE, SERVICE_SET_HVAC_MODE, @@ -27,7 +29,7 @@ from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er -from . import add_mock_config, patch_update +from . import add_mock_config async def test_climate_myzone_main( @@ -182,6 +184,7 @@ async def test_climate_myauto_main( entity_registry: er.EntityRegistry, mock_get: AsyncMock, mock_update: AsyncMock, + snapshot: SnapshotAssertion, ) -> None: """Test climate platform zone entity.""" @@ -189,27 +192,35 @@ async def test_climate_myauto_main( # Test MyAuto Climate Entity entity_id = "climate.myauto" - state = hass.states.get(entity_id) - assert state - assert state.attributes.get(ATTR_TARGET_TEMP_LOW) == 20 - assert state.attributes.get(ATTR_TARGET_TEMP_HIGH) == 24 + assert hass.states.get(entity_id) == snapshot(name=entity_id) entry = entity_registry.async_get(entity_id) assert entry assert entry.unique_id == "uniqueid-ac3" - with patch_update() as mock_update: - await hass.services.async_call( - CLIMATE_DOMAIN, - SERVICE_SET_TEMPERATURE, - { - ATTR_ENTITY_ID: [entity_id], - ATTR_TARGET_TEMP_LOW: 21, - ATTR_TARGET_TEMP_HIGH: 23, - }, - blocking=True, - ) - mock_update.assert_called_once() + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + { + ATTR_ENTITY_ID: [entity_id], + ATTR_TARGET_TEMP_LOW: 21, + ATTR_TARGET_TEMP_HIGH: 23, + }, + blocking=True, + ) + mock_update.assert_called_once() + assert mock_update.call_args[0][0] == snapshot(name=f"{entity_id}-settemp") + mock_update.reset_mock() + + # Test AutoFanMode + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_FAN_MODE, + {ATTR_ENTITY_ID: [entity_id], ATTR_FAN_MODE: FAN_AUTO}, + blocking=True, + ) + mock_update.assert_called_once() + assert mock_update.call_args[0][0] == snapshot(name=f"{entity_id}-fanmode") async def test_climate_async_failed_update( diff --git a/tests/components/advantage_air/test_switch.py b/tests/components/advantage_air/test_switch.py index a703f7edefd1..4977a4cc31f2 100644 --- a/tests/components/advantage_air/test_switch.py +++ b/tests/components/advantage_air/test_switch.py @@ -1,8 +1,9 @@ """Test the Advantage Air Switch Platform.""" - from unittest.mock import AsyncMock +from syrupy import SnapshotAssertion + from homeassistant.components.switch import ( DOMAIN as SWITCH_DOMAIN, SERVICE_TURN_OFF, @@ -20,12 +21,15 @@ async def test_cover_async_setup_entry( entity_registry: er.EntityRegistry, mock_get: AsyncMock, mock_update: AsyncMock, + snapshot: SnapshotAssertion, ) -> None: """Test switch platform.""" await add_mock_config(hass) - # Test Switch Entity + registry = er.async_get(hass) + + # Test Fresh Air Switch Entity entity_id = "switch.myzone_fresh_air" state = hass.states.get(entity_id) assert state @@ -51,6 +55,34 @@ async def test_cover_async_setup_entry( blocking=True, ) mock_update.assert_called_once() + mock_update.reset_mock() + + # Test MyFan Switch Entity + entity_id = "switch.myzone_myfan" + assert hass.states.get(entity_id) == snapshot(name=entity_id) + + entry = registry.async_get(entity_id) + assert entry + assert entry.unique_id == "uniqueid-ac1-myfan" + + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: [entity_id]}, + blocking=True, + ) + mock_update.assert_called_once() + assert mock_update.call_args[0][0] == snapshot(name=f"{entity_id}-turnon") + mock_update.reset_mock() + + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: [entity_id]}, + blocking=True, + ) + mock_update.assert_called_once() + assert mock_update.call_args[0][0] == snapshot(name=f"{entity_id}-turnoff") async def test_things_switch(