Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 42 additions & 24 deletions homeassistant/components/climate/generic_thermostat.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
SUPPORT_AWAY_MODE, SUPPORT_TARGET_TEMPERATURE, PLATFORM_SCHEMA)
from homeassistant.const import (
ATTR_UNIT_OF_MEASUREMENT, STATE_ON, STATE_OFF, ATTR_TEMPERATURE,
CONF_NAME, ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF)
CONF_NAME, ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF,
STATE_UNKNOWN)
from homeassistant.helpers import condition
from homeassistant.helpers.event import (
async_track_state_change, async_track_time_interval)
Expand All @@ -30,7 +31,6 @@

DEFAULT_TOLERANCE = 0.3
DEFAULT_NAME = 'Generic Thermostat'
DEFAULT_AWAY_TEMP = 16

CONF_HEATER = 'heater'
CONF_SENSOR = 'target_sensor'
Expand All @@ -44,7 +44,7 @@
CONF_KEEP_ALIVE = 'keep_alive'
CONF_INITIAL_OPERATION_MODE = 'initial_operation_mode'
CONF_AWAY_TEMP = 'away_temp'
SUPPORT_FLAGS = (SUPPORT_TARGET_TEMPERATURE | SUPPORT_AWAY_MODE |
SUPPORT_FLAGS = (SUPPORT_TARGET_TEMPERATURE |
SUPPORT_OPERATION_MODE)

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
Expand All @@ -64,8 +64,7 @@
cv.time_period, cv.positive_timedelta),
vol.Optional(CONF_INITIAL_OPERATION_MODE):
vol.In([STATE_AUTO, STATE_OFF]),
vol.Optional(CONF_AWAY_TEMP,
default=DEFAULT_AWAY_TEMP): vol.Coerce(float)
vol.Optional(CONF_AWAY_TEMP): vol.Coerce(float)
})


Expand Down Expand Up @@ -119,6 +118,7 @@ def __init__(self, hass, name, heater_entity_id, sensor_entity_id,
self._operation_list = [STATE_HEAT, STATE_OFF]
if initial_operation_mode == STATE_OFF:
self._enabled = False
self._current_operation = STATE_OFF
else:
self._enabled = True
self._active = False
Expand All @@ -127,6 +127,9 @@ def __init__(self, hass, name, heater_entity_id, sensor_entity_id,
self._max_temp = max_temp
self._target_temp = target_temp
self._unit = hass.config.units.temperature_unit
self._support_flags = SUPPORT_FLAGS
if away_temp is not None:
self._support_flags = SUPPORT_FLAGS | SUPPORT_AWAY_MODE
self._away_temp = away_temp
self._is_away = False

Expand All @@ -139,6 +142,10 @@ def __init__(self, hass, name, heater_entity_id, sensor_entity_id,
async_track_time_interval(
hass, self._async_keep_alive, self._keep_alive)

sensor_state = hass.states.get(sensor_entity_id)
if sensor_state and sensor_state.state != STATE_UNKNOWN:
self._async_update_temp(sensor_state)

@asyncio.coroutine
def async_added_to_hass(self):
"""Run when entity about to be added."""
Expand All @@ -154,19 +161,29 @@ def async_added_to_hass(self):
self._target_temp = self.max_temp
else:
self._target_temp = self.min_temp
_LOGGER.warning('Undefined target temperature, \
falling back to %s', self._target_temp)
_LOGGER.warning("Undefined target temperature,"
"falling back to %s", self._target_temp)
else:
self._target_temp = float(
old_state.attributes[ATTR_TEMPERATURE])
self._is_away = True if str(
old_state.attributes[ATTR_AWAY_MODE]) == STATE_ON else False
if old_state.attributes[ATTR_OPERATION_MODE] == STATE_OFF:
self._current_operation = STATE_OFF
self._enabled = False
if self._initial_operation_mode is None:
if old_state.attributes[ATTR_OPERATION_MODE] == STATE_OFF:
self._enabled = False
if old_state.attributes[ATTR_AWAY_MODE] is not None:
self._is_away = str(
old_state.attributes[ATTR_AWAY_MODE]) == STATE_ON
if (self._initial_operation_mode is None and
old_state.attributes[ATTR_OPERATION_MODE] is not None):
self._current_operation = \
old_state.attributes[ATTR_OPERATION_MODE]
if self._current_operation != STATE_OFF:
self._enabled = True
else:
# No previous state, try and restore defaults
if self._target_temp is None:
if self.ac_mode:
self._target_temp = self.max_temp
else:
self._target_temp = self.min_temp
_LOGGER.warning("No previously saved temperature, setting to %s",
self._target_temp)

@property
def state(self):
Expand Down Expand Up @@ -230,7 +247,7 @@ def set_operation_mode(self, operation_mode):
if self._is_device_active:
self._heater_turn_off()
else:
_LOGGER.error('Unrecognized operation mode: %s', operation_mode)
_LOGGER.error("Unrecognized operation mode: %s", operation_mode)
return
# Ensure we updae the current operation after changing the mode
self.schedule_update_ha_state()
Expand Down Expand Up @@ -299,16 +316,17 @@ def _async_update_temp(self, state):
self._cur_temp = self.hass.config.units.temperature(
float(state.state), unit)
except ValueError as ex:
_LOGGER.error('Unable to update from sensor: %s', ex)
_LOGGER.error("Unable to update from sensor: %s", ex)

@callback
def _async_control_heating(self):
"""Check if we need to turn heating on or off."""
if not self._active and None not in (self._cur_temp,
self._target_temp):
self._active = True
_LOGGER.info('Obtained current and target temperature. '
'Generic thermostat active.')
_LOGGER.info("Obtained current and target temperature. "
"Generic thermostat active. %s, %s",
self._cur_temp, self._target_temp)

if not self._active:
return
Expand All @@ -333,28 +351,28 @@ def _async_control_heating(self):
too_cold = self._target_temp - self._cur_temp >= \
self._cold_tolerance
if too_cold:
_LOGGER.info('Turning off AC %s', self.heater_entity_id)
_LOGGER.info("Turning off AC %s", self.heater_entity_id)
self._heater_turn_off()
else:
too_hot = self._cur_temp - self._target_temp >= \
self._hot_tolerance
if too_hot:
_LOGGER.info('Turning on AC %s', self.heater_entity_id)
_LOGGER.info("Turning on AC %s", self.heater_entity_id)
self._heater_turn_on()
else:
is_heating = self._is_device_active
if is_heating:
too_hot = self._cur_temp - self._target_temp >= \
self._hot_tolerance
if too_hot:
_LOGGER.info('Turning off heater %s',
_LOGGER.info("Turning off heater %s",
self.heater_entity_id)
self._heater_turn_off()
else:
too_cold = self._target_temp - self._cur_temp >= \
self._cold_tolerance
if too_cold:
_LOGGER.info('Turning on heater %s', self.heater_entity_id)
_LOGGER.info("Turning on heater %s", self.heater_entity_id)
self._heater_turn_on()

@property
Expand All @@ -365,7 +383,7 @@ def _is_device_active(self):
@property
def supported_features(self):
"""Return the list of supported features."""
return SUPPORT_FLAGS
return self._support_flags

@callback
def _heater_turn_on(self):
Expand Down
11 changes: 7 additions & 4 deletions tests/components/climate/test_generic_thermostat.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@ def setUp(self): # pylint: disable=invalid-name
'cold_tolerance': 2,
'hot_tolerance': 4,
'heater': ENT_SWITCH,
'target_sensor': ENT_SENSOR
'target_sensor': ENT_SENSOR,
'away_temp': 16
}})

def tearDown(self): # pylint: disable=invalid-name
Expand All @@ -176,7 +177,7 @@ def test_default_setup_params(self):
state = self.hass.states.get(ENTITY)
self.assertEqual(7, state.attributes.get('min_temp'))
self.assertEqual(35, state.attributes.get('max_temp'))
self.assertEqual(None, state.attributes.get('temperature'))
self.assertEqual(7, state.attributes.get('temperature'))

def test_get_operation_modes(self):
"""Test that the operation list returns the correct modes."""
Expand Down Expand Up @@ -266,7 +267,7 @@ def test_set_target_temp_heater_off(self):
self.hass.block_till_done()
climate.set_temperature(self.hass, 25)
self.hass.block_till_done()
self.assertEqual(1, len(self.calls))
self.assertEqual(2, len(self.calls))
call = self.calls[0]
self.assertEqual('homeassistant', call.domain)
self.assertEqual(SERVICE_TURN_OFF, call.service)
Expand Down Expand Up @@ -414,7 +415,7 @@ def test_set_target_temp_ac_off(self):
self.hass.block_till_done()
climate.set_temperature(self.hass, 30)
self.hass.block_till_done()
self.assertEqual(1, len(self.calls))
self.assertEqual(2, len(self.calls))
call = self.calls[0]
self.assertEqual('homeassistant', call.domain)
self.assertEqual(SERVICE_TURN_OFF, call.service)
Expand Down Expand Up @@ -750,6 +751,7 @@ def setUp(self): # pylint: disable=invalid-name
'cold_tolerance': 0.3,
'hot_tolerance': 0.3,
'heater': ENT_SWITCH,
'target_temp': 25,
'target_sensor': ENT_SENSOR,
'ac_mode': True,
'keep_alive': datetime.timedelta(minutes=10)
Expand Down Expand Up @@ -841,6 +843,7 @@ def setUp(self): # pylint: disable=invalid-name
'name': 'test',
'cold_tolerance': 0.3,
'hot_tolerance': 0.3,
'target_temp': 25,
'heater': ENT_SWITCH,
'target_sensor': ENT_SENSOR,
'keep_alive': datetime.timedelta(minutes=10)
Expand Down