From 24b4690e5d855be362613583a3ba6fd6f60e9929 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 26 Apr 2022 12:23:48 +0200 Subject: [PATCH] Use climate enums in zwave_js (#70757) --- homeassistant/components/zwave_js/climate.py | 90 +++++++++---------- tests/components/zwave_js/test_climate.py | 94 ++++++++++---------- 2 files changed, 87 insertions(+), 97 deletions(-) diff --git a/homeassistant/components/zwave_js/climate.py b/homeassistant/components/zwave_js/climate.py index 5d62f746490..188e627bb5e 100644 --- a/homeassistant/components/zwave_js/climate.py +++ b/homeassistant/components/zwave_js/climate.py @@ -23,24 +23,16 @@ from homeassistant.components.climate import ( DEFAULT_MAX_TEMP, DEFAULT_MIN_TEMP, ClimateEntity, - ClimateEntityFeature, ) from homeassistant.components.climate.const import ( ATTR_HVAC_MODE, ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW, - CURRENT_HVAC_COOL, - CURRENT_HVAC_FAN, - CURRENT_HVAC_HEAT, - CURRENT_HVAC_IDLE, DOMAIN as CLIMATE_DOMAIN, - HVAC_MODE_COOL, - HVAC_MODE_DRY, - HVAC_MODE_FAN_ONLY, - HVAC_MODE_HEAT, - HVAC_MODE_HEAT_COOL, - HVAC_MODE_OFF, PRESET_NONE, + ClimateEntityFeature, + HVACAction, + HVACMode, ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( @@ -63,36 +55,36 @@ from .helpers import get_value_of_zwave_value # Map Z-Wave HVAC Mode to Home Assistant value # Note: We treat "auto" as "heat_cool" as most Z-Wave devices # report auto_changeover as auto without schedule support. -ZW_HVAC_MODE_MAP: dict[int, str] = { - ThermostatMode.OFF: HVAC_MODE_OFF, - ThermostatMode.HEAT: HVAC_MODE_HEAT, - ThermostatMode.COOL: HVAC_MODE_COOL, +ZW_HVAC_MODE_MAP: dict[int, HVACMode] = { + ThermostatMode.OFF: HVACMode.OFF, + ThermostatMode.HEAT: HVACMode.HEAT, + ThermostatMode.COOL: HVACMode.COOL, # Z-Wave auto mode is actually heat/cool in the hass world - ThermostatMode.AUTO: HVAC_MODE_HEAT_COOL, - ThermostatMode.AUXILIARY: HVAC_MODE_HEAT, - ThermostatMode.FAN: HVAC_MODE_FAN_ONLY, - ThermostatMode.FURNANCE: HVAC_MODE_HEAT, - ThermostatMode.DRY: HVAC_MODE_DRY, - ThermostatMode.AUTO_CHANGE_OVER: HVAC_MODE_HEAT_COOL, - ThermostatMode.HEATING_ECON: HVAC_MODE_HEAT, - ThermostatMode.COOLING_ECON: HVAC_MODE_COOL, - ThermostatMode.AWAY: HVAC_MODE_HEAT_COOL, - ThermostatMode.FULL_POWER: HVAC_MODE_HEAT, + ThermostatMode.AUTO: HVACMode.HEAT_COOL, + ThermostatMode.AUXILIARY: HVACMode.HEAT, + ThermostatMode.FAN: HVACMode.FAN_ONLY, + ThermostatMode.FURNANCE: HVACMode.HEAT, + ThermostatMode.DRY: HVACMode.DRY, + ThermostatMode.AUTO_CHANGE_OVER: HVACMode.HEAT_COOL, + ThermostatMode.HEATING_ECON: HVACMode.HEAT, + ThermostatMode.COOLING_ECON: HVACMode.COOL, + ThermostatMode.AWAY: HVACMode.HEAT_COOL, + ThermostatMode.FULL_POWER: HVACMode.HEAT, } -HVAC_CURRENT_MAP: dict[int, str] = { - ThermostatOperatingState.IDLE: CURRENT_HVAC_IDLE, - ThermostatOperatingState.PENDING_HEAT: CURRENT_HVAC_IDLE, - ThermostatOperatingState.HEATING: CURRENT_HVAC_HEAT, - ThermostatOperatingState.PENDING_COOL: CURRENT_HVAC_IDLE, - ThermostatOperatingState.COOLING: CURRENT_HVAC_COOL, - ThermostatOperatingState.FAN_ONLY: CURRENT_HVAC_FAN, - ThermostatOperatingState.VENT_ECONOMIZER: CURRENT_HVAC_FAN, - ThermostatOperatingState.AUX_HEATING: CURRENT_HVAC_HEAT, - ThermostatOperatingState.SECOND_STAGE_HEATING: CURRENT_HVAC_HEAT, - ThermostatOperatingState.SECOND_STAGE_COOLING: CURRENT_HVAC_COOL, - ThermostatOperatingState.SECOND_STAGE_AUX_HEAT: CURRENT_HVAC_HEAT, - ThermostatOperatingState.THIRD_STAGE_AUX_HEAT: CURRENT_HVAC_HEAT, +HVAC_CURRENT_MAP: dict[int, HVACAction] = { + ThermostatOperatingState.IDLE: HVACAction.IDLE, + ThermostatOperatingState.PENDING_HEAT: HVACAction.IDLE, + ThermostatOperatingState.HEATING: HVACAction.HEATING, + ThermostatOperatingState.PENDING_COOL: HVACAction.IDLE, + ThermostatOperatingState.COOLING: HVACAction.COOLING, + ThermostatOperatingState.FAN_ONLY: HVACAction.FAN, + ThermostatOperatingState.VENT_ECONOMIZER: HVACAction.FAN, + ThermostatOperatingState.AUX_HEATING: HVACAction.HEATING, + ThermostatOperatingState.SECOND_STAGE_HEATING: HVACAction.HEATING, + ThermostatOperatingState.SECOND_STAGE_COOLING: HVACAction.COOLING, + ThermostatOperatingState.SECOND_STAGE_AUX_HEAT: HVACAction.HEATING, + ThermostatOperatingState.THIRD_STAGE_AUX_HEAT: HVACAction.HEATING, } ATTR_FAN_STATE = "fan_state" @@ -134,7 +126,7 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity): ) -> None: """Initialize thermostat.""" super().__init__(config_entry, client, info) - self._hvac_modes: dict[str, int | None] = {} + self._hvac_modes: dict[HVACMode, int | None] = {} self._hvac_presets: dict[str, int | None] = {} self._unit_value: ZwaveValue | None = None @@ -197,7 +189,7 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity): # can be set if any(self._setpoint_values.values()): self._attr_supported_features |= ClimateEntityFeature.TARGET_TEMPERATURE - if HVAC_MODE_HEAT_COOL in self.hvac_modes: + if HVACMode.HEAT_COOL in self.hvac_modes: self._attr_supported_features |= ( ClimateEntityFeature.TARGET_TEMPERATURE_RANGE ) @@ -213,7 +205,7 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity): def _set_modes_and_presets(self) -> None: """Convert Z-Wave Thermostat modes into Home Assistant modes and presets.""" - all_modes: dict[str, int | None] = {} + all_modes: dict[HVACMode, int | None] = {} all_presets: dict[str, int | None] = {PRESET_NONE: None} # Z-Wave uses one list for both modes and presets. @@ -260,23 +252,23 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity): return PRECISION_TENTHS @property - def hvac_mode(self) -> str: + def hvac_mode(self) -> HVACMode: """Return hvac operation ie. heat, cool mode.""" if self._current_mode is None: # Thermostat(valve) with no support for setting a mode is considered heating-only - return HVAC_MODE_HEAT + return HVACMode.HEAT if self._current_mode.value is None: # guard missing value - return HVAC_MODE_HEAT - return ZW_HVAC_MODE_MAP.get(int(self._current_mode.value), HVAC_MODE_HEAT_COOL) + return HVACMode.HEAT + return ZW_HVAC_MODE_MAP.get(int(self._current_mode.value), HVACMode.HEAT_COOL) @property - def hvac_modes(self) -> list[str]: + def hvac_modes(self) -> list[HVACMode]: """Return the list of available hvac operation modes.""" return list(self._hvac_modes) @property - def hvac_action(self) -> str | None: + def hvac_action(self) -> HVACAction | None: """Return the current running hvac operation if supported.""" if not self._operating_state: return None @@ -433,7 +425,7 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity): async def async_set_temperature(self, **kwargs: Any) -> None: """Set new target temperature.""" - hvac_mode: str | None = kwargs.get(ATTR_HVAC_MODE) + hvac_mode: HVACMode | None = kwargs.get(ATTR_HVAC_MODE) if hvac_mode is not None: await self.async_set_hvac_mode(hvac_mode) @@ -458,7 +450,7 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity): if target_temp_high is not None: await self.info.node.async_set_value(setpoint_high, target_temp_high) - async def async_set_hvac_mode(self, hvac_mode: str) -> None: + async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: """Set new target hvac mode.""" if (hvac_mode_id := self._hvac_modes.get(hvac_mode)) is None: raise ValueError(f"Received an invalid hvac mode: {hvac_mode}") diff --git a/tests/components/zwave_js/test_climate.py b/tests/components/zwave_js/test_climate.py index fefa680ce77..a8b19b4d5cf 100644 --- a/tests/components/zwave_js/test_climate.py +++ b/tests/components/zwave_js/test_climate.py @@ -14,23 +14,15 @@ from homeassistant.components.climate.const import ( ATTR_PRESET_MODE, ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW, - CURRENT_HVAC_COOL, - CURRENT_HVAC_IDLE, DOMAIN as CLIMATE_DOMAIN, - HVAC_MODE_COOL, - HVAC_MODE_DRY, - HVAC_MODE_HEAT, - HVAC_MODE_HEAT_COOL, - HVAC_MODE_OFF, PRESET_NONE, SERVICE_SET_FAN_MODE, SERVICE_SET_HVAC_MODE, SERVICE_SET_PRESET_MODE, SERVICE_SET_TEMPERATURE, - SUPPORT_FAN_MODE, - SUPPORT_PRESET_MODE, - SUPPORT_TARGET_TEMPERATURE, - SUPPORT_TARGET_TEMPERATURE_RANGE, + ClimateEntityFeature, + HVACAction, + HVACMode, ) from homeassistant.components.zwave_js.climate import ATTR_FAN_STATE from homeassistant.const import ( @@ -56,24 +48,24 @@ async def test_thermostat_v2( state = hass.states.get(CLIMATE_RADIO_THERMOSTAT_ENTITY) assert state - assert state.state == HVAC_MODE_HEAT + assert state.state == HVACMode.HEAT assert state.attributes[ATTR_HVAC_MODES] == [ - HVAC_MODE_OFF, - HVAC_MODE_HEAT, - HVAC_MODE_COOL, - HVAC_MODE_HEAT_COOL, + HVACMode.OFF, + HVACMode.HEAT, + HVACMode.COOL, + HVACMode.HEAT_COOL, ] assert state.attributes[ATTR_CURRENT_HUMIDITY] == 30 assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 22.2 assert state.attributes[ATTR_TEMPERATURE] == 22.2 - assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_IDLE + assert state.attributes[ATTR_HVAC_ACTION] == HVACAction.IDLE assert state.attributes[ATTR_FAN_MODE] == "Auto low" assert state.attributes[ATTR_FAN_STATE] == "Idle / off" assert ( state.attributes[ATTR_SUPPORTED_FEATURES] - == SUPPORT_TARGET_TEMPERATURE - | SUPPORT_TARGET_TEMPERATURE_RANGE - | SUPPORT_FAN_MODE + == ClimateEntityFeature.TARGET_TEMPERATURE + | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE + | ClimateEntityFeature.FAN_MODE ) client.async_send_command.reset_mock() @@ -84,7 +76,7 @@ async def test_thermostat_v2( SERVICE_SET_HVAC_MODE, { ATTR_ENTITY_ID: CLIMATE_RADIO_THERMOSTAT_ENTITY, - ATTR_HVAC_MODE: HVAC_MODE_COOL, + ATTR_HVAC_MODE: HVACMode.COOL, }, blocking=True, ) @@ -120,7 +112,7 @@ async def test_thermostat_v2( SERVICE_SET_TEMPERATURE, { ATTR_ENTITY_ID: CLIMATE_RADIO_THERMOSTAT_ENTITY, - ATTR_HVAC_MODE: HVAC_MODE_COOL, + ATTR_HVAC_MODE: HVACMode.COOL, ATTR_TEMPERATURE: 25, }, blocking=True, @@ -193,7 +185,7 @@ async def test_thermostat_v2( node.receive_event(event) state = hass.states.get(CLIMATE_RADIO_THERMOSTAT_ENTITY) - assert state.state == HVAC_MODE_COOL + assert state.state == HVACMode.COOL assert state.attributes[ATTR_TEMPERATURE] == 22.8 # Test heat_cool mode update from value updated event @@ -217,7 +209,7 @@ async def test_thermostat_v2( node.receive_event(event) state = hass.states.get(CLIMATE_RADIO_THERMOSTAT_ENTITY) - assert state.state == HVAC_MODE_HEAT_COOL + assert state.state == HVACMode.HEAT_COOL assert state.attributes[ATTR_TARGET_TEMP_HIGH] == 22.8 assert state.attributes[ATTR_TARGET_TEMP_LOW] == 22.2 @@ -289,7 +281,7 @@ async def test_thermostat_v2( SERVICE_SET_HVAC_MODE, { ATTR_ENTITY_ID: CLIMATE_RADIO_THERMOSTAT_ENTITY, - ATTR_HVAC_MODE: HVAC_MODE_DRY, + ATTR_HVAC_MODE: HVACMode.DRY, }, blocking=True, ) @@ -355,7 +347,7 @@ async def test_thermostat_different_endpoints( assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 22.8 assert state.attributes[ATTR_FAN_MODE] == "Auto low" assert state.attributes[ATTR_FAN_STATE] == "Idle / off" - assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_COOL + assert state.attributes[ATTR_HVAC_ACTION] == HVACAction.COOLING async def test_setpoint_thermostat(hass, client, climate_danfoss_lc_13, integration): @@ -364,10 +356,13 @@ async def test_setpoint_thermostat(hass, client, climate_danfoss_lc_13, integrat state = hass.states.get(CLIMATE_DANFOSS_LC13_ENTITY) assert state - assert state.state == HVAC_MODE_HEAT + assert state.state == HVACMode.HEAT assert state.attributes[ATTR_TEMPERATURE] == 14 - assert state.attributes[ATTR_HVAC_MODES] == [HVAC_MODE_HEAT] - assert state.attributes[ATTR_SUPPORTED_FEATURES] == SUPPORT_TARGET_TEMPERATURE + assert state.attributes[ATTR_HVAC_MODES] == [HVACMode.HEAT] + assert ( + state.attributes[ATTR_SUPPORTED_FEATURES] + == ClimateEntityFeature.TARGET_TEMPERATURE + ) client.async_send_command_no_wait.reset_mock() @@ -389,19 +384,19 @@ async def test_setpoint_thermostat(hass, client, climate_danfoss_lc_13, integrat SERVICE_SET_HVAC_MODE, { ATTR_ENTITY_ID: CLIMATE_DANFOSS_LC13_ENTITY, - ATTR_HVAC_MODE: HVAC_MODE_COOL, + ATTR_HVAC_MODE: HVACMode.COOL, }, blocking=True, ) - # Test that setting HVAC_MODE_HEAT works. If the no-op logic didn't work, this would + # Test that setting HVACMode.HEAT works. If the no-op logic didn't work, this would # raise an error await hass.services.async_call( CLIMATE_DOMAIN, SERVICE_SET_HVAC_MODE, { ATTR_ENTITY_ID: CLIMATE_DANFOSS_LC13_ENTITY, - ATTR_HVAC_MODE: HVAC_MODE_HEAT, + ATTR_HVAC_MODE: HVACMode.HEAT, }, blocking=True, ) @@ -455,7 +450,7 @@ async def test_setpoint_thermostat(hass, client, climate_danfoss_lc_13, integrat node.receive_event(event) state = hass.states.get(CLIMATE_DANFOSS_LC13_ENTITY) - assert state.state == HVAC_MODE_HEAT + assert state.state == HVACMode.HEAT assert state.attributes[ATTR_TEMPERATURE] == 23 client.async_send_command_no_wait.reset_mock() @@ -479,15 +474,18 @@ async def test_thermostat_heatit_z_trm3( state = hass.states.get(CLIMATE_FLOOR_THERMOSTAT_ENTITY) assert state - assert state.state == HVAC_MODE_HEAT + assert state.state == HVACMode.HEAT assert state.attributes[ATTR_HVAC_MODES] == [ - HVAC_MODE_OFF, - HVAC_MODE_HEAT, + HVACMode.OFF, + HVACMode.HEAT, ] assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 22.9 assert state.attributes[ATTR_TEMPERATURE] == 22.5 - assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_IDLE - assert state.attributes[ATTR_SUPPORTED_FEATURES] == SUPPORT_TARGET_TEMPERATURE + assert state.attributes[ATTR_HVAC_ACTION] == HVACAction.IDLE + assert ( + state.attributes[ATTR_SUPPORTED_FEATURES] + == ClimateEntityFeature.TARGET_TEMPERATURE + ) assert state.attributes[ATTR_MIN_TEMP] == 5 assert state.attributes[ATTR_MAX_TEMP] == 35 @@ -546,17 +544,17 @@ async def test_thermostat_heatit_z_trm2fx( state = hass.states.get(CLIMATE_FLOOR_THERMOSTAT_ENTITY) assert state - assert state.state == HVAC_MODE_HEAT + assert state.state == HVACMode.HEAT assert state.attributes[ATTR_HVAC_MODES] == [ - HVAC_MODE_OFF, - HVAC_MODE_HEAT, - HVAC_MODE_COOL, + HVACMode.OFF, + HVACMode.HEAT, + HVACMode.COOL, ] assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 28.8 assert state.attributes[ATTR_TEMPERATURE] == 29 assert ( state.attributes[ATTR_SUPPORTED_FEATURES] - == SUPPORT_TARGET_TEMPERATURE | SUPPORT_PRESET_MODE + == ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE ) assert state.attributes[ATTR_MIN_TEMP] == 7 assert state.attributes[ATTR_MAX_TEMP] == 35 @@ -593,10 +591,10 @@ async def test_thermostat_srt321_hrt4_zw(hass, client, srt321_hrt4_zw, integrati state = hass.states.get(CLIMATE_MAIN_HEAT_ACTIONNER) assert state - assert state.state == HVAC_MODE_OFF + assert state.state == HVACMode.OFF assert state.attributes[ATTR_HVAC_MODES] == [ - HVAC_MODE_OFF, - HVAC_MODE_HEAT, + HVACMode.OFF, + HVACMode.HEAT, ] assert state.attributes[ATTR_CURRENT_TEMPERATURE] is None assert state.attributes[ATTR_SUPPORTED_FEATURES] == 0 @@ -610,7 +608,7 @@ async def test_preset_and_no_setpoint( state = hass.states.get(CLIMATE_EUROTRONICS_SPIRIT_Z_ENTITY) assert state - assert state.state == HVAC_MODE_HEAT + assert state.state == HVACMode.HEAT assert state.attributes[ATTR_TEMPERATURE] == 22 # Test setting preset mode Full power @@ -676,7 +674,7 @@ async def test_preset_and_no_setpoint( node.receive_event(event) state = hass.states.get(CLIMATE_EUROTRONICS_SPIRIT_Z_ENTITY) - assert state.state == HVAC_MODE_HEAT + assert state.state == HVACMode.HEAT assert state.attributes[ATTR_TEMPERATURE] is None assert state.attributes[ATTR_PRESET_MODE] == "Full power"