From 34097cda245775fe6f39c1cc0834a9e395f746d7 Mon Sep 17 00:00:00 2001 From: Lewis Juggins <ldjuggins@gmail.com> Date: Sun, 27 Nov 2016 09:31:00 +0000 Subject: [PATCH] Allow generic thermostat tolerance to be customisable to determine the temperature difference required to turn switch on. (#4585) --- .../components/climate/generic_thermostat.py | 17 ++++++---- .../climate/test_generic_thermostat.py | 31 ++++++++++++++++++- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/climate/generic_thermostat.py b/homeassistant/components/climate/generic_thermostat.py index 1a0b20dc11e..1b3d20d8b59 100644 --- a/homeassistant/components/climate/generic_thermostat.py +++ b/homeassistant/components/climate/generic_thermostat.py @@ -21,10 +21,10 @@ _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['switch', 'sensor'] -TOL_TEMP = 0.3 +DEFAULT_TOLERANCE = 0.3 +DEFAULT_NAME = 'Generic Thermostat' CONF_NAME = 'name' -DEFAULT_NAME = 'Generic Thermostat' CONF_HEATER = 'heater' CONF_SENSOR = 'target_sensor' CONF_MIN_TEMP = 'min_temp' @@ -32,6 +32,7 @@ CONF_MAX_TEMP = 'max_temp' CONF_TARGET_TEMP = 'target_temp' CONF_AC_MODE = 'ac_mode' CONF_MIN_DUR = 'min_cycle_duration' +CONF_TOLERANCE = 'tolerance' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ @@ -42,6 +43,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_MIN_DUR): vol.All(cv.time_period, cv.positive_timedelta), vol.Optional(CONF_MIN_TEMP): vol.Coerce(float), vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_TOLERANCE, default=DEFAULT_TOLERANCE): vol.Coerce(float), vol.Optional(CONF_TARGET_TEMP): vol.Coerce(float), }) @@ -56,23 +58,26 @@ def setup_platform(hass, config, add_devices, discovery_info=None): target_temp = config.get(CONF_TARGET_TEMP) ac_mode = config.get(CONF_AC_MODE) min_cycle_duration = config.get(CONF_MIN_DUR) + tolerance = config.get(CONF_TOLERANCE) add_devices([GenericThermostat( hass, name, heater_entity_id, sensor_entity_id, min_temp, max_temp, - target_temp, ac_mode, min_cycle_duration)]) + target_temp, ac_mode, min_cycle_duration, tolerance)]) class GenericThermostat(ClimateDevice): """Representation of a GenericThermostat device.""" def __init__(self, hass, name, heater_entity_id, sensor_entity_id, - min_temp, max_temp, target_temp, ac_mode, min_cycle_duration): + min_temp, max_temp, target_temp, ac_mode, min_cycle_duration, + tolerance): """Initialize the thermostat.""" self.hass = hass self._name = name self.heater_entity_id = heater_entity_id self.ac_mode = ac_mode self.min_cycle_duration = min_cycle_duration + self._tolerance = tolerance self._active = False self._cur_temp = None @@ -193,7 +198,7 @@ class GenericThermostat(ClimateDevice): return if self.ac_mode: - too_hot = self._cur_temp - self._target_temp > TOL_TEMP + too_hot = self._cur_temp - self._target_temp > self._tolerance is_cooling = self._is_device_active if too_hot and not is_cooling: _LOGGER.info('Turning on AC %s', self.heater_entity_id) @@ -202,7 +207,7 @@ class GenericThermostat(ClimateDevice): _LOGGER.info('Turning off AC %s', self.heater_entity_id) switch.turn_off(self.hass, self.heater_entity_id) else: - too_cold = self._target_temp - self._cur_temp > TOL_TEMP + too_cold = self._target_temp - self._cur_temp > self._tolerance is_heating = self._is_device_active if too_cold and not is_heating: diff --git a/tests/components/climate/test_generic_thermostat.py b/tests/components/climate/test_generic_thermostat.py index d11d925ef41..1730c3e003b 100644 --- a/tests/components/climate/test_generic_thermostat.py +++ b/tests/components/climate/test_generic_thermostat.py @@ -25,6 +25,7 @@ ENT_SWITCH = 'switch.test' MIN_TEMP = 3.0 MAX_TEMP = 65.0 TARGET_TEMP = 42.0 +TOLERANCE = 0.5 class TestSetupClimateGenericThermostat(unittest.TestCase): @@ -84,6 +85,7 @@ class TestClimateGenericThermostat(unittest.TestCase): assert setup_component(self.hass, climate.DOMAIN, {'climate': { 'platform': 'generic_thermostat', 'name': 'test', + 'tolerance': 2, 'heater': ENT_SWITCH, 'target_sensor': ENT_SENSOR }}) @@ -113,7 +115,7 @@ class TestClimateGenericThermostat(unittest.TestCase): 'target_sensor': ENT_SENSOR, 'min_temp': MIN_TEMP, 'max_temp': MAX_TEMP, - 'target_temp': TARGET_TEMP + 'target_temp': TARGET_TEMP, }}) state = self.hass.states.get(ENTITY) self.assertEqual(MIN_TEMP, state.attributes.get('min_temp')) @@ -205,6 +207,30 @@ class TestClimateGenericThermostat(unittest.TestCase): self.assertEqual(SERVICE_TURN_OFF, call.service) self.assertEqual(ENT_SWITCH, call.data['entity_id']) + def test_temp_change_heater_on_within_tolerance(self): + """Test if temperature change turn heater on within tolerance.""" + self._setup_switch(False) + climate.set_temperature(self.hass, 30) + self.hass.block_till_done() + self._setup_sensor(29) + self.hass.block_till_done() + self.assertEqual(0, len(self.calls)) + + def test_temp_change_heater_on_outside_tolerance(self): + """Test if temperature change doesn't turn heater on outside + tolerance. + """ + self._setup_switch(False) + climate.set_temperature(self.hass, 30) + self.hass.block_till_done() + self._setup_sensor(25) + self.hass.block_till_done() + self.assertEqual(1, len(self.calls)) + call = self.calls[0] + self.assertEqual('switch', call.domain) + self.assertEqual(SERVICE_TURN_ON, call.service) + self.assertEqual(ENT_SWITCH, call.data['entity_id']) + def _setup_sensor(self, temp, unit=TEMP_CELSIUS): """Setup the test sensor.""" self.hass.states.set(ENT_SENSOR, temp, { @@ -235,6 +261,7 @@ class TestClimateGenericThermostatACMode(unittest.TestCase): assert setup_component(self.hass, climate.DOMAIN, {'climate': { 'platform': 'generic_thermostat', 'name': 'test', + 'tolerance': 0.3, 'heater': ENT_SWITCH, 'target_sensor': ENT_SENSOR, 'ac_mode': True @@ -326,6 +353,7 @@ class TestClimateGenericThermostatACModeMinCycle(unittest.TestCase): assert setup_component(self.hass, climate.DOMAIN, {'climate': { 'platform': 'generic_thermostat', 'name': 'test', + 'tolerance': 0.3, 'heater': ENT_SWITCH, 'target_sensor': ENT_SENSOR, 'ac_mode': True, @@ -418,6 +446,7 @@ class TestClimateGenericThermostatMinCycle(unittest.TestCase): assert setup_component(self.hass, climate.DOMAIN, {'climate': { 'platform': 'generic_thermostat', 'name': 'test', + 'tolerance': 0.3, 'heater': ENT_SWITCH, 'target_sensor': ENT_SENSOR, 'min_cycle_duration': datetime.timedelta(minutes=10) -- GitLab