From cb298123d400c8c7d4db26103a42aa4863a5a554 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Sat, 8 Jul 2017 12:21:10 -0400 Subject: [PATCH] Add set_operation_mode support to generic_thermostat (#8392) This commit adds support for the set_operation_mode system call to the generic thermostat component. This enables users to set whether the thermostat is enabled or not by either setting it to auto or off. --- .../components/climate/generic_thermostat.py | 28 +++++++++- .../climate/test_generic_thermostat.py | 54 +++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/climate/generic_thermostat.py b/homeassistant/components/climate/generic_thermostat.py index 6e7ba3cffedc93..9442b7da194462 100644 --- a/homeassistant/components/climate/generic_thermostat.py +++ b/homeassistant/components/climate/generic_thermostat.py @@ -12,7 +12,8 @@ from homeassistant.core import callback from homeassistant.components import switch from homeassistant.components.climate import ( - STATE_HEAT, STATE_COOL, STATE_IDLE, ClimateDevice, PLATFORM_SCHEMA) + STATE_HEAT, STATE_COOL, STATE_IDLE, ClimateDevice, PLATFORM_SCHEMA, + STATE_AUTO) from homeassistant.const import ( ATTR_UNIT_OF_MEASUREMENT, STATE_ON, STATE_OFF, ATTR_TEMPERATURE, CONF_NAME) @@ -87,6 +88,7 @@ def __init__(self, hass, name, heater_entity_id, sensor_entity_id, self.min_cycle_duration = min_cycle_duration self._tolerance = tolerance self._keep_alive = keep_alive + self._enabled = True self._active = False self._cur_temp = None @@ -131,6 +133,8 @@ def current_temperature(self): @property def current_operation(self): """Return current operation ie. heat, cool, idle.""" + if not self._enabled: + return STATE_OFF if self.ac_mode: cooling = self._active and self._is_device_active return STATE_COOL if cooling else STATE_IDLE @@ -143,6 +147,25 @@ def target_temperature(self): """Return the temperature we try to reach.""" return self._target_temp + @property + def operation_list(self): + """List of available operation modes.""" + return [STATE_AUTO, STATE_OFF] + + def set_operation_mode(self, operation_mode): + """Set operation mode.""" + if operation_mode == STATE_AUTO: + self._enabled = True + elif operation_mode == STATE_OFF: + self._enabled = False + if self._is_device_active: + switch.async_turn_off(self.hass, self.heater_entity_id) + else: + _LOGGER.error('Unrecognized operation mode: %s', operation_mode) + return + # Ensure we updae the current operation after changing the mode + self.schedule_update_ha_state() + @asyncio.coroutine def async_set_temperature(self, **kwargs): """Set new target temperature.""" @@ -221,6 +244,9 @@ def _async_control_heating(self): if not self._active: return + if not self._enabled: + return + if self.min_cycle_duration: if self._is_device_active: current_state = STATE_ON diff --git a/tests/components/climate/test_generic_thermostat.py b/tests/components/climate/test_generic_thermostat.py index 16dbe5ae89553b..15fc3f6a98277c 100644 --- a/tests/components/climate/test_generic_thermostat.py +++ b/tests/components/climate/test_generic_thermostat.py @@ -108,6 +108,12 @@ def test_default_setup_params(self): self.assertEqual(35, state.attributes.get('max_temp')) self.assertEqual(None, state.attributes.get('temperature')) + def test_get_operation_modes(self): + """Test that the operation list returns the correct modes.""" + state = self.hass.states.get(ENTITY) + modes = state.attributes.get('operation_list') + self.assertEqual([climate.STATE_AUTO, STATE_OFF], modes) + def test_set_target_temp(self): """Test the setting of the target temperature.""" climate.set_temperature(self.hass, 30) @@ -211,6 +217,30 @@ def test_temp_change_heater_off_outside_tolerance(self): self.assertEqual(SERVICE_TURN_OFF, call.service) self.assertEqual(ENT_SWITCH, call.data['entity_id']) + def test_running_when_operating_mode_is_off(self): + """Test that the switch turns off when enabled is set False.""" + self._setup_switch(True) + climate.set_temperature(self.hass, 30) + self.hass.block_till_done() + climate.set_operation_mode(self.hass, STATE_OFF) + self.hass.block_till_done() + self.assertEqual(1, len(self.calls)) + call = self.calls[0] + self.assertEqual('switch', call.domain) + self.assertEqual(SERVICE_TURN_OFF, call.service) + self.assertEqual(ENT_SWITCH, call.data['entity_id']) + + def test_no_state_change_when_operation_mode_off(self): + """Test that the switch doesn't turn on when enabled is False.""" + self._setup_switch(False) + climate.set_temperature(self.hass, 30) + self.hass.block_till_done() + climate.set_operation_mode(self.hass, STATE_OFF) + self.hass.block_till_done() + self._setup_sensor(25) + self.hass.block_till_done() + self.assertEqual(0, len(self.calls)) + def _setup_sensor(self, temp, unit=TEMP_CELSIUS): """Setup the test sensor.""" self.hass.states.set(ENT_SENSOR, temp, { @@ -321,6 +351,30 @@ def test_temp_change_ac_on_outside_tolerance(self): self.assertEqual(SERVICE_TURN_ON, call.service) self.assertEqual(ENT_SWITCH, call.data['entity_id']) + def test_running_when_operating_mode_is_off(self): + """Test that the switch turns off when enabled is set False.""" + self._setup_switch(True) + climate.set_temperature(self.hass, 30) + self.hass.block_till_done() + climate.set_operation_mode(self.hass, STATE_OFF) + self.hass.block_till_done() + self.assertEqual(1, len(self.calls)) + call = self.calls[0] + self.assertEqual('switch', call.domain) + self.assertEqual(SERVICE_TURN_OFF, call.service) + self.assertEqual(ENT_SWITCH, call.data['entity_id']) + + def test_no_state_change_when_operation_mode_off(self): + """Test that the switch doesn't turn on when enabled is False.""" + self._setup_switch(False) + climate.set_temperature(self.hass, 30) + self.hass.block_till_done() + climate.set_operation_mode(self.hass, STATE_OFF) + self.hass.block_till_done() + self._setup_sensor(35) + self.hass.block_till_done() + self.assertEqual(0, len(self.calls)) + def _setup_sensor(self, temp, unit=TEMP_CELSIUS): """Setup the test sensor.""" self.hass.states.set(ENT_SENSOR, temp, {