Skip to content
Snippets Groups Projects
Commit 34097cda authored by Lewis Juggins's avatar Lewis Juggins Committed by GitHub
Browse files

Allow generic thermostat tolerance to be customisable to determine the...

Allow generic thermostat tolerance to be customisable to determine the temperature difference required to turn switch on. (#4585)
parent 0ce3703e
Branches
Tags
No related merge requests found
...@@ -21,10 +21,10 @@ _LOGGER = logging.getLogger(__name__) ...@@ -21,10 +21,10 @@ _LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['switch', 'sensor'] DEPENDENCIES = ['switch', 'sensor']
TOL_TEMP = 0.3 DEFAULT_TOLERANCE = 0.3
DEFAULT_NAME = 'Generic Thermostat'
CONF_NAME = 'name' CONF_NAME = 'name'
DEFAULT_NAME = 'Generic Thermostat'
CONF_HEATER = 'heater' CONF_HEATER = 'heater'
CONF_SENSOR = 'target_sensor' CONF_SENSOR = 'target_sensor'
CONF_MIN_TEMP = 'min_temp' CONF_MIN_TEMP = 'min_temp'
...@@ -32,6 +32,7 @@ CONF_MAX_TEMP = 'max_temp' ...@@ -32,6 +32,7 @@ CONF_MAX_TEMP = 'max_temp'
CONF_TARGET_TEMP = 'target_temp' CONF_TARGET_TEMP = 'target_temp'
CONF_AC_MODE = 'ac_mode' CONF_AC_MODE = 'ac_mode'
CONF_MIN_DUR = 'min_cycle_duration' CONF_MIN_DUR = 'min_cycle_duration'
CONF_TOLERANCE = 'tolerance'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
...@@ -42,6 +43,7 @@ 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_DUR): vol.All(cv.time_period, cv.positive_timedelta),
vol.Optional(CONF_MIN_TEMP): vol.Coerce(float), vol.Optional(CONF_MIN_TEMP): vol.Coerce(float),
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, 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), vol.Optional(CONF_TARGET_TEMP): vol.Coerce(float),
}) })
...@@ -56,23 +58,26 @@ def setup_platform(hass, config, add_devices, discovery_info=None): ...@@ -56,23 +58,26 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
target_temp = config.get(CONF_TARGET_TEMP) target_temp = config.get(CONF_TARGET_TEMP)
ac_mode = config.get(CONF_AC_MODE) ac_mode = config.get(CONF_AC_MODE)
min_cycle_duration = config.get(CONF_MIN_DUR) min_cycle_duration = config.get(CONF_MIN_DUR)
tolerance = config.get(CONF_TOLERANCE)
add_devices([GenericThermostat( add_devices([GenericThermostat(
hass, name, heater_entity_id, sensor_entity_id, min_temp, max_temp, 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): class GenericThermostat(ClimateDevice):
"""Representation of a GenericThermostat device.""" """Representation of a GenericThermostat device."""
def __init__(self, hass, name, heater_entity_id, sensor_entity_id, 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.""" """Initialize the thermostat."""
self.hass = hass self.hass = hass
self._name = name self._name = name
self.heater_entity_id = heater_entity_id self.heater_entity_id = heater_entity_id
self.ac_mode = ac_mode self.ac_mode = ac_mode
self.min_cycle_duration = min_cycle_duration self.min_cycle_duration = min_cycle_duration
self._tolerance = tolerance
self._active = False self._active = False
self._cur_temp = None self._cur_temp = None
...@@ -193,7 +198,7 @@ class GenericThermostat(ClimateDevice): ...@@ -193,7 +198,7 @@ class GenericThermostat(ClimateDevice):
return return
if self.ac_mode: 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 is_cooling = self._is_device_active
if too_hot and not is_cooling: if too_hot and not is_cooling:
_LOGGER.info('Turning on AC %s', self.heater_entity_id) _LOGGER.info('Turning on AC %s', self.heater_entity_id)
...@@ -202,7 +207,7 @@ class GenericThermostat(ClimateDevice): ...@@ -202,7 +207,7 @@ class GenericThermostat(ClimateDevice):
_LOGGER.info('Turning off AC %s', self.heater_entity_id) _LOGGER.info('Turning off AC %s', self.heater_entity_id)
switch.turn_off(self.hass, self.heater_entity_id) switch.turn_off(self.hass, self.heater_entity_id)
else: 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 is_heating = self._is_device_active
if too_cold and not is_heating: if too_cold and not is_heating:
......
...@@ -25,6 +25,7 @@ ENT_SWITCH = 'switch.test' ...@@ -25,6 +25,7 @@ ENT_SWITCH = 'switch.test'
MIN_TEMP = 3.0 MIN_TEMP = 3.0
MAX_TEMP = 65.0 MAX_TEMP = 65.0
TARGET_TEMP = 42.0 TARGET_TEMP = 42.0
TOLERANCE = 0.5
class TestSetupClimateGenericThermostat(unittest.TestCase): class TestSetupClimateGenericThermostat(unittest.TestCase):
...@@ -84,6 +85,7 @@ class TestClimateGenericThermostat(unittest.TestCase): ...@@ -84,6 +85,7 @@ class TestClimateGenericThermostat(unittest.TestCase):
assert setup_component(self.hass, climate.DOMAIN, {'climate': { assert setup_component(self.hass, climate.DOMAIN, {'climate': {
'platform': 'generic_thermostat', 'platform': 'generic_thermostat',
'name': 'test', 'name': 'test',
'tolerance': 2,
'heater': ENT_SWITCH, 'heater': ENT_SWITCH,
'target_sensor': ENT_SENSOR 'target_sensor': ENT_SENSOR
}}) }})
...@@ -113,7 +115,7 @@ class TestClimateGenericThermostat(unittest.TestCase): ...@@ -113,7 +115,7 @@ class TestClimateGenericThermostat(unittest.TestCase):
'target_sensor': ENT_SENSOR, 'target_sensor': ENT_SENSOR,
'min_temp': MIN_TEMP, 'min_temp': MIN_TEMP,
'max_temp': MAX_TEMP, 'max_temp': MAX_TEMP,
'target_temp': TARGET_TEMP 'target_temp': TARGET_TEMP,
}}) }})
state = self.hass.states.get(ENTITY) state = self.hass.states.get(ENTITY)
self.assertEqual(MIN_TEMP, state.attributes.get('min_temp')) self.assertEqual(MIN_TEMP, state.attributes.get('min_temp'))
...@@ -205,6 +207,30 @@ class TestClimateGenericThermostat(unittest.TestCase): ...@@ -205,6 +207,30 @@ class TestClimateGenericThermostat(unittest.TestCase):
self.assertEqual(SERVICE_TURN_OFF, call.service) self.assertEqual(SERVICE_TURN_OFF, call.service)
self.assertEqual(ENT_SWITCH, call.data['entity_id']) 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): def _setup_sensor(self, temp, unit=TEMP_CELSIUS):
"""Setup the test sensor.""" """Setup the test sensor."""
self.hass.states.set(ENT_SENSOR, temp, { self.hass.states.set(ENT_SENSOR, temp, {
...@@ -235,6 +261,7 @@ class TestClimateGenericThermostatACMode(unittest.TestCase): ...@@ -235,6 +261,7 @@ class TestClimateGenericThermostatACMode(unittest.TestCase):
assert setup_component(self.hass, climate.DOMAIN, {'climate': { assert setup_component(self.hass, climate.DOMAIN, {'climate': {
'platform': 'generic_thermostat', 'platform': 'generic_thermostat',
'name': 'test', 'name': 'test',
'tolerance': 0.3,
'heater': ENT_SWITCH, 'heater': ENT_SWITCH,
'target_sensor': ENT_SENSOR, 'target_sensor': ENT_SENSOR,
'ac_mode': True 'ac_mode': True
...@@ -326,6 +353,7 @@ class TestClimateGenericThermostatACModeMinCycle(unittest.TestCase): ...@@ -326,6 +353,7 @@ class TestClimateGenericThermostatACModeMinCycle(unittest.TestCase):
assert setup_component(self.hass, climate.DOMAIN, {'climate': { assert setup_component(self.hass, climate.DOMAIN, {'climate': {
'platform': 'generic_thermostat', 'platform': 'generic_thermostat',
'name': 'test', 'name': 'test',
'tolerance': 0.3,
'heater': ENT_SWITCH, 'heater': ENT_SWITCH,
'target_sensor': ENT_SENSOR, 'target_sensor': ENT_SENSOR,
'ac_mode': True, 'ac_mode': True,
...@@ -418,6 +446,7 @@ class TestClimateGenericThermostatMinCycle(unittest.TestCase): ...@@ -418,6 +446,7 @@ class TestClimateGenericThermostatMinCycle(unittest.TestCase):
assert setup_component(self.hass, climate.DOMAIN, {'climate': { assert setup_component(self.hass, climate.DOMAIN, {'climate': {
'platform': 'generic_thermostat', 'platform': 'generic_thermostat',
'name': 'test', 'name': 'test',
'tolerance': 0.3,
'heater': ENT_SWITCH, 'heater': ENT_SWITCH,
'target_sensor': ENT_SENSOR, 'target_sensor': ENT_SENSOR,
'min_cycle_duration': datetime.timedelta(minutes=10) 'min_cycle_duration': datetime.timedelta(minutes=10)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment