diff --git a/homeassistant/components/climate/generic_thermostat.py b/homeassistant/components/climate/generic_thermostat.py index fdfe56ca62c..9445fc7cfc9 100644 --- a/homeassistant/components/climate/generic_thermostat.py +++ b/homeassistant/components/climate/generic_thermostat.py @@ -17,7 +17,8 @@ from homeassistant.components.climate import ( 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) @@ -30,7 +31,6 @@ DEPENDENCIES = ['switch', 'sensor'] DEFAULT_TOLERANCE = 0.3 DEFAULT_NAME = 'Generic Thermostat' -DEFAULT_AWAY_TEMP = 16 CONF_HEATER = 'heater' CONF_SENSOR = 'target_sensor' @@ -44,7 +44,7 @@ CONF_HOT_TOLERANCE = 'hot_tolerance' 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({ @@ -64,8 +64,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 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) }) @@ -119,6 +118,7 @@ class GenericThermostat(ClimateDevice): 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 @@ -127,6 +127,9 @@ class GenericThermostat(ClimateDevice): 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 @@ -139,6 +142,10 @@ class GenericThermostat(ClimateDevice): 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.""" @@ -154,19 +161,29 @@ class GenericThermostat(ClimateDevice): 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): @@ -230,7 +247,7 @@ class GenericThermostat(ClimateDevice): 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() @@ -299,7 +316,7 @@ class GenericThermostat(ClimateDevice): 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): @@ -307,8 +324,9 @@ class GenericThermostat(ClimateDevice): 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 @@ -333,13 +351,13 @@ class GenericThermostat(ClimateDevice): 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 @@ -347,14 +365,14 @@ class GenericThermostat(ClimateDevice): 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 @@ -365,7 +383,7 @@ class GenericThermostat(ClimateDevice): @property def supported_features(self): """Return the list of supported features.""" - return SUPPORT_FLAGS + return self._support_flags @callback def _heater_turn_on(self): diff --git a/tests/components/climate/test_generic_thermostat.py b/tests/components/climate/test_generic_thermostat.py index 776e79a6827..190eb7e8522 100644 --- a/tests/components/climate/test_generic_thermostat.py +++ b/tests/components/climate/test_generic_thermostat.py @@ -160,7 +160,8 @@ class TestClimateGenericThermostat(unittest.TestCase): '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 @@ -176,7 +177,7 @@ class TestClimateGenericThermostat(unittest.TestCase): 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.""" @@ -266,7 +267,7 @@ class TestClimateGenericThermostat(unittest.TestCase): 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) @@ -414,7 +415,7 @@ class TestClimateGenericThermostatACMode(unittest.TestCase): 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) @@ -750,6 +751,7 @@ class TestClimateGenericThermostatACKeepAlive(unittest.TestCase): '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) @@ -841,6 +843,7 @@ class TestClimateGenericThermostatKeepAlive(unittest.TestCase): '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)