diff --git a/homeassistant/components/whirlpool/climate.py b/homeassistant/components/whirlpool/climate.py index 811b05435bd..bd8cb0505fa 100644 --- a/homeassistant/components/whirlpool/climate.py +++ b/homeassistant/components/whirlpool/climate.py @@ -48,6 +48,18 @@ AIRCON_FANSPEED_MAP = { FAN_MODE_TO_AIRCON_FANSPEED = {v: k for k, v in AIRCON_FANSPEED_MAP.items()} +SUPPORTED_FAN_MODES = [FAN_AUTO, FAN_HIGH, FAN_MEDIUM, FAN_LOW, FAN_OFF] +SUPPORTED_HVAC_MODES = [ + HVAC_MODE_COOL, + HVAC_MODE_HEAT, + HVAC_MODE_FAN_ONLY, + HVAC_MODE_OFF, +] +SUPPORTED_MAX_TEMP = 30 +SUPPORTED_MIN_TEMP = 16 +SUPPORTED_SWING_MODES = [SWING_HORIZONTAL, SWING_OFF] +SUPPORTED_TARGET_TEMPERATURE_STEP = 1 + async def async_setup_entry(hass, config_entry, async_add_entities): """Set up entry.""" @@ -66,20 +78,15 @@ async def async_setup_entry(hass, config_entry, async_add_entities): class AirConEntity(ClimateEntity): """Representation of an air conditioner.""" - _attr_fan_modes = [FAN_AUTO, FAN_HIGH, FAN_MEDIUM, FAN_LOW, FAN_OFF] - _attr_hvac_modes = [ - HVAC_MODE_COOL, - HVAC_MODE_HEAT, - HVAC_MODE_FAN_ONLY, - HVAC_MODE_OFF, - ] - _attr_max_temp = 30 - _attr_min_temp = 16 + _attr_fan_modes = SUPPORTED_FAN_MODES + _attr_hvac_modes = SUPPORTED_HVAC_MODES + _attr_max_temp = SUPPORTED_MAX_TEMP + _attr_min_temp = SUPPORTED_MIN_TEMP _attr_supported_features = ( SUPPORT_TARGET_TEMPERATURE | SUPPORT_FAN_MODE | SUPPORT_SWING_MODE ) - _attr_swing_modes = [SWING_HORIZONTAL, SWING_OFF] - _attr_target_temperature_step = 1 + _attr_swing_modes = SUPPORTED_SWING_MODES + _attr_target_temperature_step = SUPPORTED_TARGET_TEMPERATURE_STEP _attr_temperature_unit = TEMP_CELSIUS _attr_should_poll = False @@ -141,7 +148,7 @@ class AirConEntity(ClimateEntity): return HVAC_MODE_OFF mode: AirconMode = self._aircon.get_mode() - return AIRCON_MODE_MAP.get(mode, None) + return AIRCON_MODE_MAP.get(mode) async def async_set_hvac_mode(self, hvac_mode): """Set HVAC mode.""" @@ -151,8 +158,7 @@ class AirConEntity(ClimateEntity): mode = HVAC_MODE_TO_AIRCON_MODE.get(hvac_mode) if not mode: - _LOGGER.warning("Unexpected hvac mode: %s", hvac_mode) - return + raise ValueError(f"Invalid hvac mode {hvac_mode}") await self._aircon.set_mode(mode) if not self._aircon.get_power_on(): @@ -168,7 +174,7 @@ class AirConEntity(ClimateEntity): """Set fan mode.""" fanspeed = FAN_MODE_TO_AIRCON_FANSPEED.get(fan_mode) if not fanspeed: - return + raise ValueError(f"Invalid fan mode {fan_mode}") await self._aircon.set_fanspeed(fanspeed) @property diff --git a/homeassistant/components/whirlpool/config_flow.py b/homeassistant/components/whirlpool/config_flow.py index c7ec37290cb..ac6cb3d568e 100644 --- a/homeassistant/components/whirlpool/config_flow.py +++ b/homeassistant/components/whirlpool/config_flow.py @@ -1,4 +1,6 @@ """Config flow for Whirlpool Sixth Sense integration.""" +from __future__ import annotations + import asyncio import logging @@ -13,10 +15,14 @@ from .const import DOMAIN _LOGGER = logging.getLogger(__name__) -STEP_USER_DATA_SCHEMA = vol.Schema({CONF_USERNAME: str, CONF_PASSWORD: str}) +STEP_USER_DATA_SCHEMA = vol.Schema( + {vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str} +) -async def validate_input(hass: core.HomeAssistant, data): +async def validate_input( + hass: core.HomeAssistant, data: dict[str, str] +) -> dict[str, str]: """Validate the user input allows us to connect. Data has the keys from STEP_USER_DATA_SCHEMA with values provided by the user. diff --git a/tests/components/whirlpool/test_climate.py b/tests/components/whirlpool/test_climate.py index 77009607947..314c6c2685b 100644 --- a/tests/components/whirlpool/test_climate.py +++ b/tests/components/whirlpool/test_climate.py @@ -2,6 +2,7 @@ from unittest.mock import AsyncMock, MagicMock import aiohttp +import pytest import whirlpool from homeassistant.components.climate.const import ( @@ -271,13 +272,14 @@ async def test_service_calls(hass: HomeAssistant, mock_aircon_api: MagicMock): ) mock_aircon_api.return_value.set_mode.reset_mock() - # HVAC_MODE_DRY should be ignored - await hass.services.async_call( - CLIMATE_DOMAIN, - SERVICE_SET_HVAC_MODE, - {ATTR_ENTITY_ID: "climate.said1", ATTR_HVAC_MODE: HVAC_MODE_DRY}, - blocking=True, - ) + # HVAC_MODE_DRY is not supported + with pytest.raises(ValueError): + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + {ATTR_ENTITY_ID: "climate.said1", ATTR_HVAC_MODE: HVAC_MODE_DRY}, + blocking=True, + ) mock_aircon_api.return_value.set_mode.assert_not_called() mock_aircon_api.return_value.set_mode.reset_mock() @@ -325,13 +327,14 @@ async def test_service_calls(hass: HomeAssistant, mock_aircon_api: MagicMock): ) mock_aircon_api.return_value.set_fanspeed.reset_mock() - # FAN_MIDDLE should be ignored - await hass.services.async_call( - CLIMATE_DOMAIN, - SERVICE_SET_FAN_MODE, - {ATTR_ENTITY_ID: "climate.said1", ATTR_FAN_MODE: FAN_MIDDLE}, - blocking=True, - ) + # FAN_MIDDLE is not supported + with pytest.raises(ValueError): + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_FAN_MODE, + {ATTR_ENTITY_ID: "climate.said1", ATTR_FAN_MODE: FAN_MIDDLE}, + blocking=True, + ) mock_aircon_api.return_value.set_fanspeed.assert_not_called() mock_aircon_api.return_value.set_fanspeed.reset_mock() diff --git a/tests/components/whirlpool/test_config_flow.py b/tests/components/whirlpool/test_config_flow.py index 6746e406a85..721a4da9383 100644 --- a/tests/components/whirlpool/test_config_flow.py +++ b/tests/components/whirlpool/test_config_flow.py @@ -7,6 +7,8 @@ import aiohttp from homeassistant import config_entries from homeassistant.components.whirlpool.const import DOMAIN +from tests.common import MockConfigEntry + async def test_form(hass): """Test we get the form.""" @@ -120,3 +122,36 @@ async def test_form_generic_auth_exception(hass): ) assert result2["type"] == "form" assert result2["errors"] == {"base": "unknown"} + + +async def test_form_already_configured(hass): + """Test we handle cannot connect error.""" + mock_entry = MockConfigEntry( + domain=DOMAIN, + data={"username": "test-username", "password": "test-password"}, + unique_id="test-username", + ) + mock_entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + assert result["type"] == "form" + assert result["step_id"] == config_entries.SOURCE_USER + + with patch("homeassistant.components.whirlpool.config_flow.Auth.do_auth"), patch( + "homeassistant.components.whirlpool.config_flow.Auth.is_access_token_valid", + return_value=True, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "username": "test-username", + "password": "test-password", + }, + ) + await hass.async_block_till_done() + + assert result2["type"] == "abort" + assert result2["reason"] == "already_configured"