diff --git a/custom_components/versatile_thermostat/thermostat_valve.py b/custom_components/versatile_thermostat/thermostat_valve.py index fa4e42b..09d45a9 100644 --- a/custom_components/versatile_thermostat/thermostat_valve.py +++ b/custom_components/versatile_thermostat/thermostat_valve.py @@ -31,7 +31,10 @@ class ThermostatOverValve(BaseThermostat): @property def valve_open_percent(self) -> int: """ Gives the percentage of valve needed""" - return round(max(0, min(self.proportional_algorithm.on_percent, 1)) * 100) + if self._hvac_mode == HVACMode.OFF: + return 0 + else: + return round(max(0, min(self.proportional_algorithm.on_percent, 1)) * 100) def post_init(self, entry_infos): """ Initialize the Thermostat""" diff --git a/custom_components/versatile_thermostat/underlyings.py b/custom_components/versatile_thermostat/underlyings.py index a39e99f..5d51314 100644 --- a/custom_components/versatile_thermostat/underlyings.py +++ b/custom_components/versatile_thermostat/underlyings.py @@ -658,13 +658,34 @@ class UnderlyingValve(UnderlyingEntity): self._hvac_mode = None self._percent_open = self._thermostat.valve_open_percent + async def send_percent_open(self): + """ Send the percent open to the underlying valve """ + # This may fails if called after shutdown + try: + data = { "value": self._percent_open } + await self._hass.services.async_call( + HA_DOMAIN, + SERVICE_SET_VALUE, + data, + ) + except ServiceNotFound as err: + _LOGGER.error(err) + + async def turn_off(self): + """Turn heater toggleable device off.""" + _LOGGER.debug("%s - Stopping underlying valve entity %s", self, self._entity_id) + self._percent_open = 0 + if self.is_device_active: + await self.send_percent_open() + + async def turn_on(self): + """Nothing to do for Valve because it cannot be turned off""" + async def set_hvac_mode(self, hvac_mode: HVACMode) -> bool: """Set the HVACmode. Returns true if something have change""" if hvac_mode == HVACMode.OFF: - if self.is_device_active: - await self.turn_off() - self._cancel_cycle() + await self.turn_off() if self._hvac_mode != hvac_mode: self._hvac_mode = hvac_mode @@ -701,12 +722,7 @@ class UnderlyingValve(UnderlyingEntity): try: _LOGGER.info("%s - Setting valve ouverture percent to %s", self, self._percent_open) - data = { "value": self._percent_open } - await self._hass.services.async_call( - HA_DOMAIN, - SERVICE_SET_VALUE, - data, - ) + await self.send_percent_open() except ServiceNotFound as err: _LOGGER.error(err) diff --git a/tests/test_valve.py b/tests/test_valve.py index 62c905c..138c942 100644 --- a/tests/test_valve.py +++ b/tests/test_valve.py @@ -160,11 +160,12 @@ async def test_over_valve_full_start(hass: HomeAssistant, skip_hass_states_is_st ) as mock_send_event, patch( "homeassistant.core.ServiceRegistry.async_call" ) as mock_service_call, patch( - "custom_components.versatile_thermostat.underlyings.UnderlyingValve.is_device_active", return_value=True + "homeassistant.core.StateMachine.get", return_value=90 ): # Change temperature event_timestamp = now - timedelta(minutes=10) await send_temperature_change_event(entity, 15, datetime.now()) + assert entity.valve_open_percent == 90 await send_ext_temperature_change_event(entity, 10, datetime.now()) # Should heating strongly now assert entity.valve_open_percent == 98 @@ -191,35 +192,60 @@ async def test_over_valve_full_start(hass: HomeAssistant, skip_hass_states_is_st assert mock_send_event.call_count == 0 - # ICI + # Change to preset Comfort + await entity.async_set_preset_mode(preset_mode=PRESET_COMFORT) + assert entity.preset_mode == PRESET_COMFORT + assert entity.target_temperature == 17.2 + assert entity.valve_open_percent == 73 + assert entity.is_device_active is True + assert entity.hvac_action == HVACAction.HEATING + # Change presence to on event_timestamp = now - timedelta(minutes=4) await send_presence_change_event(entity, True, False, event_timestamp) - assert entity._presence_state == STATE_ON # pylint: disable=protected-access - - await entity.async_set_hvac_mode(HVACMode.COOL) - assert entity.hvac_mode is HVACMode.COOL - - await entity.async_set_preset_mode(PRESET_COMFORT) + assert entity.presence_state == STATE_ON # pylint: disable=protected-access assert entity.preset_mode is PRESET_COMFORT - assert entity.target_temperature == 23 + assert entity.target_temperature == 19 + assert entity.valve_open_percent == 100 # Full heating + assert entity.is_device_active is True + assert entity.hvac_action == HVACAction.HEATING + # Change internal temperature + with patch( + "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event" + ) as mock_send_event, patch( + "homeassistant.core.ServiceRegistry.async_call" + ) as mock_service_call, patch( + "homeassistant.core.StateMachine.get", return_value=0 + ): + event_timestamp = now - timedelta(minutes=3) + await send_temperature_change_event(entity, 20, datetime.now()) + assert entity.valve_open_percent == 0 + assert entity.is_device_active is False + assert entity.hvac_action == HVACAction.IDLE + + + await send_temperature_change_event(entity, 17, datetime.now()) # switch to Eco await entity.async_set_preset_mode(PRESET_ECO) assert entity.preset_mode is PRESET_ECO - assert entity.target_temperature == 25 + assert entity.target_temperature == 17 + assert entity.valve_open_percent == 7 # Unset the presence - event_timestamp = now - timedelta(minutes=3) + event_timestamp = now - timedelta(minutes=2) await send_presence_change_event(entity, False, True, event_timestamp) - assert entity._presence_state == STATE_OFF # pylint: disable=protected-access - assert entity.target_temperature == 27 # eco_ac_away + assert entity.presence_state == STATE_OFF # pylint: disable=protected-access + assert entity.valve_open_percent == 10 + assert entity.target_temperature == 17.1 # eco_away + assert entity.is_device_active is False + assert entity.hvac_action == HVACAction.IDLE # Open a window with patch( "homeassistant.helpers.condition.state", return_value=True ): - event_timestamp = now - timedelta(minutes=2) + event_timestamp = now - timedelta(minutes=1) try_condition = await send_window_change_event(entity, True, False, event_timestamp) # Confirme the window event @@ -227,18 +253,20 @@ async def test_over_valve_full_start(hass: HomeAssistant, skip_hass_states_is_st assert entity.hvac_mode is HVACMode.OFF assert entity.hvac_action is HVACAction.OFF - assert entity.target_temperature == 27 # eco_ac_away + assert entity.target_temperature == 17.1 # eco + assert entity.valve_open_percent == 0 # Close a window with patch( "homeassistant.helpers.condition.state", return_value=True ): - event_timestamp = now - timedelta(minutes=2) + event_timestamp = now - timedelta(minutes=0) try_condition = await send_window_change_event(entity, False, True, event_timestamp) # Confirme the window event await try_condition(None) - assert entity.hvac_mode is HVACMode.COOL + assert entity.hvac_mode is HVACMode.HEAT assert (entity.hvac_action is HVACAction.OFF or entity.hvac_action is HVACAction.IDLE) - assert entity.target_temperature == 27 # eco_ac_away + assert entity.target_temperature == 17.1 # eco + assert entity.valve_open_percent == 10