Changed single tolerance value to COLD and HOT tolerances. Allows on and off states to have different error bands. (#9843)
parent
6b96bc3859
commit
c0eaf0386c
|
@ -36,7 +36,8 @@ 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'
|
CONF_COLD_TOLERANCE = 'cold_tolerance'
|
||||||
|
CONF_HOT_TOLERANCE = 'hot_tolerance'
|
||||||
CONF_KEEP_ALIVE = 'keep_alive'
|
CONF_KEEP_ALIVE = 'keep_alive'
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,7 +49,10 @@ 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_COLD_TOLERANCE, default=DEFAULT_TOLERANCE): vol.Coerce(
|
||||||
|
float),
|
||||||
|
vol.Optional(CONF_HOT_TOLERANCE, default=DEFAULT_TOLERANCE): vol.Coerce(
|
||||||
|
float),
|
||||||
vol.Optional(CONF_TARGET_TEMP): vol.Coerce(float),
|
vol.Optional(CONF_TARGET_TEMP): vol.Coerce(float),
|
||||||
vol.Optional(CONF_KEEP_ALIVE): vol.All(
|
vol.Optional(CONF_KEEP_ALIVE): vol.All(
|
||||||
cv.time_period, cv.positive_timedelta),
|
cv.time_period, cv.positive_timedelta),
|
||||||
|
@ -66,12 +70,14 @@ def async_setup_platform(hass, config, async_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)
|
cold_tolerance = config.get(CONF_COLD_TOLERANCE)
|
||||||
|
hot_tolerance = config.get(CONF_HOT_TOLERANCE)
|
||||||
keep_alive = config.get(CONF_KEEP_ALIVE)
|
keep_alive = config.get(CONF_KEEP_ALIVE)
|
||||||
|
|
||||||
async_add_devices([GenericThermostat(
|
async_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, tolerance, keep_alive)])
|
target_temp, ac_mode, min_cycle_duration, cold_tolerance,
|
||||||
|
hot_tolerance, keep_alive)])
|
||||||
|
|
||||||
|
|
||||||
class GenericThermostat(ClimateDevice):
|
class GenericThermostat(ClimateDevice):
|
||||||
|
@ -79,14 +85,15 @@ class GenericThermostat(ClimateDevice):
|
||||||
|
|
||||||
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, keep_alive):
|
cold_tolerance, hot_tolerance, keep_alive):
|
||||||
"""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._cold_tolerance = cold_tolerance
|
||||||
|
self._hot_tolerance = hot_tolerance
|
||||||
self._keep_alive = keep_alive
|
self._keep_alive = keep_alive
|
||||||
self._enabled = True
|
self._enabled = True
|
||||||
|
|
||||||
|
@ -261,25 +268,29 @@ class GenericThermostat(ClimateDevice):
|
||||||
if self.ac_mode:
|
if self.ac_mode:
|
||||||
is_cooling = self._is_device_active
|
is_cooling = self._is_device_active
|
||||||
if is_cooling:
|
if is_cooling:
|
||||||
too_cold = self._target_temp - self._cur_temp > self._tolerance
|
too_cold = self._target_temp - self._cur_temp >= \
|
||||||
|
self._cold_tolerance
|
||||||
if too_cold:
|
if too_cold:
|
||||||
_LOGGER.info('Turning off AC %s', self.heater_entity_id)
|
_LOGGER.info('Turning off AC %s', self.heater_entity_id)
|
||||||
switch.async_turn_off(self.hass, self.heater_entity_id)
|
switch.async_turn_off(self.hass, self.heater_entity_id)
|
||||||
else:
|
else:
|
||||||
too_hot = self._cur_temp - self._target_temp > self._tolerance
|
too_hot = self._cur_temp - self._target_temp >= \
|
||||||
|
self._hot_tolerance
|
||||||
if too_hot:
|
if too_hot:
|
||||||
_LOGGER.info('Turning on AC %s', self.heater_entity_id)
|
_LOGGER.info('Turning on AC %s', self.heater_entity_id)
|
||||||
switch.async_turn_on(self.hass, self.heater_entity_id)
|
switch.async_turn_on(self.hass, self.heater_entity_id)
|
||||||
else:
|
else:
|
||||||
is_heating = self._is_device_active
|
is_heating = self._is_device_active
|
||||||
if is_heating:
|
if is_heating:
|
||||||
too_hot = self._cur_temp - self._target_temp > self._tolerance
|
too_hot = self._cur_temp - self._target_temp >= \
|
||||||
|
self._hot_tolerance
|
||||||
if too_hot:
|
if too_hot:
|
||||||
_LOGGER.info('Turning off heater %s',
|
_LOGGER.info('Turning off heater %s',
|
||||||
self.heater_entity_id)
|
self.heater_entity_id)
|
||||||
switch.async_turn_off(self.hass, self.heater_entity_id)
|
switch.async_turn_off(self.hass, self.heater_entity_id)
|
||||||
else:
|
else:
|
||||||
too_cold = self._target_temp - self._cur_temp > self._tolerance
|
too_cold = self._target_temp - self._cur_temp >= \
|
||||||
|
self._cold_tolerance
|
||||||
if too_cold:
|
if too_cold:
|
||||||
_LOGGER.info('Turning on heater %s', self.heater_entity_id)
|
_LOGGER.info('Turning on heater %s', self.heater_entity_id)
|
||||||
switch.async_turn_on(self.hass, self.heater_entity_id)
|
switch.async_turn_on(self.hass, self.heater_entity_id)
|
||||||
|
|
|
@ -28,7 +28,8 @@ 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
|
COLD_TOLERANCE = 0.5
|
||||||
|
HOT_TOLERANCE = 0.5
|
||||||
|
|
||||||
|
|
||||||
class TestSetupClimateGenericThermostat(unittest.TestCase):
|
class TestSetupClimateGenericThermostat(unittest.TestCase):
|
||||||
|
@ -88,7 +89,8 @@ 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,
|
'cold_tolerance': 2,
|
||||||
|
'hot_tolerance': 4,
|
||||||
'heater': ENT_SWITCH,
|
'heater': ENT_SWITCH,
|
||||||
'target_sensor': ENT_SENSOR
|
'target_sensor': ENT_SENSOR
|
||||||
}})
|
}})
|
||||||
|
@ -183,11 +185,11 @@ class TestClimateGenericThermostat(unittest.TestCase):
|
||||||
self.assertEqual(0, len(self.calls))
|
self.assertEqual(0, len(self.calls))
|
||||||
|
|
||||||
def test_temp_change_heater_on_outside_tolerance(self):
|
def test_temp_change_heater_on_outside_tolerance(self):
|
||||||
"""Test if temperature change turn heater on outside tolerance."""
|
"""Test if temperature change turn heater on outside cold tolerance."""
|
||||||
self._setup_switch(False)
|
self._setup_switch(False)
|
||||||
climate.set_temperature(self.hass, 30)
|
climate.set_temperature(self.hass, 30)
|
||||||
self.hass.block_till_done()
|
self.hass.block_till_done()
|
||||||
self._setup_sensor(25)
|
self._setup_sensor(27)
|
||||||
self.hass.block_till_done()
|
self.hass.block_till_done()
|
||||||
self.assertEqual(1, len(self.calls))
|
self.assertEqual(1, len(self.calls))
|
||||||
call = self.calls[0]
|
call = self.calls[0]
|
||||||
|
@ -200,12 +202,12 @@ class TestClimateGenericThermostat(unittest.TestCase):
|
||||||
self._setup_switch(True)
|
self._setup_switch(True)
|
||||||
climate.set_temperature(self.hass, 30)
|
climate.set_temperature(self.hass, 30)
|
||||||
self.hass.block_till_done()
|
self.hass.block_till_done()
|
||||||
self._setup_sensor(31)
|
self._setup_sensor(33)
|
||||||
self.hass.block_till_done()
|
self.hass.block_till_done()
|
||||||
self.assertEqual(0, len(self.calls))
|
self.assertEqual(0, len(self.calls))
|
||||||
|
|
||||||
def test_temp_change_heater_off_outside_tolerance(self):
|
def test_temp_change_heater_off_outside_tolerance(self):
|
||||||
"""Test if temperature change turn heater off outside tolerance."""
|
"""Test if temperature change turn heater off outside hot tolerance."""
|
||||||
self._setup_switch(True)
|
self._setup_switch(True)
|
||||||
climate.set_temperature(self.hass, 30)
|
climate.set_temperature(self.hass, 30)
|
||||||
self.hass.block_till_done()
|
self.hass.block_till_done()
|
||||||
|
@ -271,7 +273,8 @@ 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,
|
'cold_tolerance': 2,
|
||||||
|
'hot_tolerance': 4,
|
||||||
'heater': ENT_SWITCH,
|
'heater': ENT_SWITCH,
|
||||||
'target_sensor': ENT_SENSOR,
|
'target_sensor': ENT_SENSOR,
|
||||||
'ac_mode': True
|
'ac_mode': True
|
||||||
|
@ -321,7 +324,7 @@ class TestClimateGenericThermostatACMode(unittest.TestCase):
|
||||||
self._setup_switch(True)
|
self._setup_switch(True)
|
||||||
climate.set_temperature(self.hass, 30)
|
climate.set_temperature(self.hass, 30)
|
||||||
self.hass.block_till_done()
|
self.hass.block_till_done()
|
||||||
self._setup_sensor(25)
|
self._setup_sensor(27)
|
||||||
self.hass.block_till_done()
|
self.hass.block_till_done()
|
||||||
self.assertEqual(1, len(self.calls))
|
self.assertEqual(1, len(self.calls))
|
||||||
call = self.calls[0]
|
call = self.calls[0]
|
||||||
|
@ -405,7 +408,8 @@ 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,
|
'cold_tolerance': 0.3,
|
||||||
|
'hot_tolerance': 0.3,
|
||||||
'heater': ENT_SWITCH,
|
'heater': ENT_SWITCH,
|
||||||
'target_sensor': ENT_SENSOR,
|
'target_sensor': ENT_SENSOR,
|
||||||
'ac_mode': True,
|
'ac_mode': True,
|
||||||
|
@ -498,7 +502,8 @@ 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,
|
'cold_tolerance': 0.3,
|
||||||
|
'hot_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)
|
||||||
|
@ -590,7 +595,8 @@ class TestClimateGenericThermostatACKeepAlive(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,
|
'cold_tolerance': 0.3,
|
||||||
|
'hot_tolerance': 0.3,
|
||||||
'heater': ENT_SWITCH,
|
'heater': ENT_SWITCH,
|
||||||
'target_sensor': ENT_SENSOR,
|
'target_sensor': ENT_SENSOR,
|
||||||
'ac_mode': True,
|
'ac_mode': True,
|
||||||
|
@ -681,7 +687,8 @@ class TestClimateGenericThermostatKeepAlive(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,
|
'cold_tolerance': 0.3,
|
||||||
|
'hot_tolerance': 0.3,
|
||||||
'heater': ENT_SWITCH,
|
'heater': ENT_SWITCH,
|
||||||
'target_sensor': ENT_SENSOR,
|
'target_sensor': ENT_SENSOR,
|
||||||
'keep_alive': datetime.timedelta(minutes=10)
|
'keep_alive': datetime.timedelta(minutes=10)
|
||||||
|
|
Loading…
Reference in New Issue