diff --git a/homeassistant/components/comelit/climate.py b/homeassistant/components/comelit/climate.py index 69d95da01bf..6b05ed80b13 100644 --- a/homeassistant/components/comelit/climate.py +++ b/homeassistant/components/comelit/climate.py @@ -20,7 +20,12 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback -from .const import DOMAIN +from .const import ( + DOMAIN, + PRESET_MODE_AUTO, + PRESET_MODE_AUTO_TARGET_TEMP, + PRESET_MODE_MANUAL, +) from .coordinator import ComelitConfigEntry, ComelitSerialBridge from .entity import ComelitBridgeBaseEntity from .utils import bridge_api_call @@ -41,11 +46,13 @@ class ClimaComelitMode(StrEnum): class ClimaComelitCommand(StrEnum): """Serial Bridge clima commands.""" + AUTO = "auto" + MANUAL = "man" OFF = "off" ON = "on" - MANUAL = "man" SET = "set" - AUTO = "auto" + SNOW = "lower" + SUN = "upper" class ClimaComelitApiStatus(TypedDict): @@ -67,11 +74,15 @@ API_STATUS: dict[str, ClimaComelitApiStatus] = { ), } -MODE_TO_ACTION: dict[HVACMode, ClimaComelitCommand] = { +HVACMODE_TO_ACTION: dict[HVACMode, ClimaComelitCommand] = { HVACMode.OFF: ClimaComelitCommand.OFF, - HVACMode.AUTO: ClimaComelitCommand.AUTO, - HVACMode.COOL: ClimaComelitCommand.MANUAL, - HVACMode.HEAT: ClimaComelitCommand.MANUAL, + HVACMode.COOL: ClimaComelitCommand.SNOW, + HVACMode.HEAT: ClimaComelitCommand.SUN, +} + +PRESET_MODE_TO_ACTION: dict[str, ClimaComelitCommand] = { + PRESET_MODE_MANUAL: ClimaComelitCommand.MANUAL, + PRESET_MODE_AUTO: ClimaComelitCommand.AUTO, } @@ -93,17 +104,20 @@ async def async_setup_entry( class ComelitClimateEntity(ComelitBridgeBaseEntity, ClimateEntity): """Climate device.""" - _attr_hvac_modes = [HVACMode.AUTO, HVACMode.COOL, HVACMode.HEAT, HVACMode.OFF] + _attr_hvac_modes = [HVACMode.COOL, HVACMode.HEAT, HVACMode.OFF] + _attr_preset_modes = [PRESET_MODE_AUTO, PRESET_MODE_MANUAL] _attr_max_temp = 30 _attr_min_temp = 5 _attr_supported_features = ( ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON + | ClimateEntityFeature.PRESET_MODE ) _attr_target_temperature_step = PRECISION_TENTHS _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_name = None + _attr_translation_key = "thermostat" def __init__( self, @@ -132,6 +146,8 @@ class ComelitClimateEntity(ComelitBridgeBaseEntity, ClimateEntity): _mode = values[2] # Values from API: "O", "L", "U" _automatic = values[3] == ClimaComelitMode.AUTO + self._attr_preset_mode = PRESET_MODE_AUTO if _automatic else PRESET_MODE_MANUAL + self._attr_current_temperature = values[0] / 10 self._attr_hvac_action = None @@ -141,10 +157,6 @@ class ComelitClimateEntity(ComelitBridgeBaseEntity, ClimateEntity): self._attr_hvac_action = API_STATUS[_mode]["hvac_action"] self._attr_hvac_mode = None - if _mode == ClimaComelitMode.OFF: - self._attr_hvac_mode = HVACMode.OFF - if _automatic: - self._attr_hvac_mode = HVACMode.AUTO if _mode in API_STATUS: self._attr_hvac_mode = API_STATUS[_mode]["hvac_mode"] @@ -160,13 +172,12 @@ class ComelitClimateEntity(ComelitBridgeBaseEntity, ClimateEntity): async def async_set_temperature(self, **kwargs: Any) -> None: """Set new target temperature.""" if ( - target_temp := kwargs.get(ATTR_TEMPERATURE) - ) is None or self.hvac_mode == HVACMode.OFF: + (target_temp := kwargs.get(ATTR_TEMPERATURE)) is None + or self.hvac_mode == HVACMode.OFF + or self._attr_preset_mode == PRESET_MODE_AUTO + ): return - await self.coordinator.api.set_clima_status( - self._device.index, ClimaComelitCommand.MANUAL - ) await self.coordinator.api.set_clima_status( self._device.index, ClimaComelitCommand.SET, target_temp ) @@ -177,12 +188,28 @@ class ComelitClimateEntity(ComelitBridgeBaseEntity, ClimateEntity): async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: """Set hvac mode.""" - if hvac_mode != HVACMode.OFF: + if self._attr_hvac_mode == HVACMode.OFF: await self.coordinator.api.set_clima_status( self._device.index, ClimaComelitCommand.ON ) await self.coordinator.api.set_clima_status( - self._device.index, MODE_TO_ACTION[hvac_mode] + self._device.index, HVACMODE_TO_ACTION[hvac_mode] ) self._attr_hvac_mode = hvac_mode self.async_write_ha_state() + + async def async_set_preset_mode(self, preset_mode: str) -> None: + """Set new target preset mode.""" + + if self._attr_hvac_mode == HVACMode.OFF: + return + + await self.coordinator.api.set_clima_status( + self._device.index, PRESET_MODE_TO_ACTION[preset_mode] + ) + self._attr_preset_mode = preset_mode + + if preset_mode == PRESET_MODE_AUTO: + self._attr_target_temperature = PRESET_MODE_AUTO_TARGET_TEMP + + self.async_write_ha_state() diff --git a/homeassistant/components/comelit/const.py b/homeassistant/components/comelit/const.py index f52f33fd6da..4baaf0ee426 100644 --- a/homeassistant/components/comelit/const.py +++ b/homeassistant/components/comelit/const.py @@ -11,3 +11,8 @@ DEFAULT_PORT = 80 DEVICE_TYPE_LIST = [BRIDGE, VEDO] SCAN_INTERVAL = 5 + +PRESET_MODE_AUTO = "automatic" +PRESET_MODE_MANUAL = "manual" + +PRESET_MODE_AUTO_TARGET_TEMP = 20 diff --git a/homeassistant/components/comelit/icons.json b/homeassistant/components/comelit/icons.json index 6c42d20de65..6ac83cfc8e0 100644 --- a/homeassistant/components/comelit/icons.json +++ b/homeassistant/components/comelit/icons.json @@ -4,6 +4,18 @@ "zone_status": { "default": "mdi:shield-check" } + }, + "climate": { + "thermostat": { + "state_attributes": { + "preset_mode": { + "state": { + "automatic": "mdi:refresh-auto", + "manual": "mdi:alpha-m" + } + } + } + } } } } diff --git a/homeassistant/components/comelit/strings.json b/homeassistant/components/comelit/strings.json index 7a04b5d2d04..d63d22f307a 100644 --- a/homeassistant/components/comelit/strings.json +++ b/homeassistant/components/comelit/strings.json @@ -74,6 +74,18 @@ "dehumidifier": { "name": "Dehumidifier" } + }, + "climate": { + "thermostat": { + "state_attributes": { + "preset_mode": { + "state": { + "automatic": "[%key:common::state::auto%]", + "manual": "[%key:common::state::manual%]" + } + } + } + } } }, "exceptions": { diff --git a/tests/components/comelit/snapshots/test_climate.ambr b/tests/components/comelit/snapshots/test_climate.ambr index 0233359bc45..1f8ce4a3caf 100644 --- a/tests/components/comelit/snapshots/test_climate.ambr +++ b/tests/components/comelit/snapshots/test_climate.ambr @@ -6,13 +6,16 @@ 'area_id': None, 'capabilities': dict({ 'hvac_modes': list([ - , , , , ]), 'max_temp': 30, 'min_temp': 5, + 'preset_modes': list([ + 'automatic', + 'manual', + ]), 'target_temp_step': 0.1, }), 'config_entry_id': , @@ -37,8 +40,8 @@ 'original_name': None, 'platform': 'comelit', 'previous_unique_id': None, - 'supported_features': , - 'translation_key': None, + 'supported_features': , + 'translation_key': 'thermostat', 'unique_id': 'serial_bridge_config_entry_id-0', 'unit_of_measurement': None, }) @@ -50,14 +53,18 @@ 'friendly_name': 'Climate0', 'hvac_action': , 'hvac_modes': list([ - , , , , ]), 'max_temp': 30, 'min_temp': 5, - 'supported_features': , + 'preset_mode': 'manual', + 'preset_modes': list([ + 'automatic', + 'manual', + ]), + 'supported_features': , 'target_temp_step': 0.1, 'temperature': 5.0, }), diff --git a/tests/components/comelit/test_climate.py b/tests/components/comelit/test_climate.py index 1938211c9dd..5027106cb5b 100644 --- a/tests/components/comelit/test_climate.py +++ b/tests/components/comelit/test_climate.py @@ -11,12 +11,19 @@ from syrupy.assertion import SnapshotAssertion from homeassistant.components.climate import ( ATTR_HVAC_MODE, + ATTR_PRESET_MODE, DOMAIN as CLIMATE_DOMAIN, SERVICE_SET_HVAC_MODE, + SERVICE_SET_PRESET_MODE, SERVICE_SET_TEMPERATURE, + SERVICE_TURN_OFF, HVACMode, ) -from homeassistant.components.comelit.const import SCAN_INTERVAL +from homeassistant.components.comelit.const import ( + PRESET_MODE_AUTO, + PRESET_MODE_MANUAL, + SCAN_INTERVAL, +) from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE, Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er @@ -273,10 +280,75 @@ async def test_climate_hvac_mode_when_off( await hass.services.async_call( CLIMATE_DOMAIN, SERVICE_SET_HVAC_MODE, - {ATTR_ENTITY_ID: ENTITY_ID, ATTR_HVAC_MODE: HVACMode.AUTO}, + {ATTR_ENTITY_ID: ENTITY_ID, ATTR_HVAC_MODE: HVACMode.COOL}, blocking=True, ) mock_serial_bridge.set_clima_status.assert_called() assert (state := hass.states.get(ENTITY_ID)) - assert state.state == HVACMode.AUTO + assert state.state == HVACMode.COOL + + +async def test_climate_preset_mode( + hass: HomeAssistant, + mock_serial_bridge: AsyncMock, + mock_serial_bridge_config_entry: MockConfigEntry, +) -> None: + """Test climate preset mode service.""" + + await setup_integration(hass, mock_serial_bridge_config_entry) + + assert (state := hass.states.get(ENTITY_ID)) + assert state.state == HVACMode.HEAT + assert state.attributes[ATTR_TEMPERATURE] == 5.0 + assert state.attributes[ATTR_PRESET_MODE] == PRESET_MODE_MANUAL + + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_PRESET_MODE, + {ATTR_ENTITY_ID: ENTITY_ID, ATTR_PRESET_MODE: PRESET_MODE_AUTO}, + blocking=True, + ) + mock_serial_bridge.set_clima_status.assert_called() + + assert (state := hass.states.get(ENTITY_ID)) + assert state.state == HVACMode.HEAT + assert state.attributes[ATTR_TEMPERATURE] == 20.0 + assert state.attributes[ATTR_PRESET_MODE] == PRESET_MODE_AUTO + + +async def test_climate_preset_mode_when_off( + hass: HomeAssistant, + mock_serial_bridge: AsyncMock, + mock_serial_bridge_config_entry: MockConfigEntry, +) -> None: + """Test climate preset mode service when off.""" + + await setup_integration(hass, mock_serial_bridge_config_entry) + + assert (state := hass.states.get(ENTITY_ID)) + assert state.state == HVACMode.HEAT + assert state.attributes[ATTR_TEMPERATURE] == 5.0 + assert state.attributes[ATTR_PRESET_MODE] == PRESET_MODE_MANUAL + + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: ENTITY_ID}, + blocking=True, + ) + mock_serial_bridge.set_clima_status.assert_called() + + assert (state := hass.states.get(ENTITY_ID)) + assert state.state == HVACMode.OFF + + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_PRESET_MODE, + {ATTR_ENTITY_ID: ENTITY_ID, ATTR_PRESET_MODE: PRESET_MODE_AUTO}, + blocking=True, + ) + mock_serial_bridge.set_clima_status.assert_called() + + assert (state := hass.states.get(ENTITY_ID)) + assert state.state == HVACMode.OFF