Additional support for ecobee hold mode (#6258)

* Integrate suggestion in #5590 by nordlead2005. This change has been
sitting in limbo for over a month, but it is a good idea. I don't
mean to step on nordlead2005's toes, but we need to make progress.

* Use defined constant for TEMPERATURE_HOLD

* Integrate handling of vacation into hold mode. Canceling vacation
hold requires an update to the external pyecobee library. Creation
of vacation is not supported (it would be straightforward in the code,
but a complex user interface would be required, similar to what is
now done in the ecobee thermostat).

* Add capability to retrieve list of defined climates from ecobee.

* The mode() method used to return the system mode in internal
representation. However, the user sees a different notation in
the ecobee thermostat. Seeing some internal name is particularly
weired with user-defined climates, where these are named "smart1",
"smart2", etc., instead of the name the user has defined. Return
the user-defined name instead. This change might break some user
interfaces but is easily remedied (e.g., use "Away" instead of
"away").

* Simplify is_away_mode_on().

* Correction of erroneously indented else statement.

* Change comment as flake8 gets confused.
This commit is contained in:
Duoxilian 2017-03-02 01:52:31 -06:00 committed by Paulus Schoutsen
parent f3870a8a48
commit e14d6f11c6

View File

@ -25,6 +25,8 @@ ATTR_FAN_MIN_ON_TIME = 'fan_min_on_time'
ATTR_RESUME_ALL = 'resume_all' ATTR_RESUME_ALL = 'resume_all'
DEFAULT_RESUME_ALL = False DEFAULT_RESUME_ALL = False
TEMPERATURE_HOLD = 'temp'
VACATION_HOLD = 'vacation'
DEPENDENCIES = ['ecobee'] DEPENDENCIES = ['ecobee']
@ -112,6 +114,8 @@ class Thermostat(ClimateDevice):
self.thermostat_index) self.thermostat_index)
self._name = self.thermostat['name'] self._name = self.thermostat['name']
self.hold_temp = hold_temp self.hold_temp = hold_temp
self.vacation = None
self._climate_list = self.climate_list
self._operation_list = ['auto', 'auxHeatOnly', 'cool', self._operation_list = ['auto', 'auxHeatOnly', 'cool',
'heat', 'off'] 'heat', 'off']
self.update_without_throttle = False self.update_without_throttle = False
@ -187,29 +191,30 @@ class Thermostat(ClimateDevice):
def current_hold_mode(self): def current_hold_mode(self):
"""Return current hold mode.""" """Return current hold mode."""
events = self.thermostat['events'] events = self.thermostat['events']
if any((event['holdClimateRef'] == 'away' and for event in events:
int(event['endDate'][0:4])-int(event['startDate'][0:4]) <= 1) if event['running']:
or event['type'] == 'autoAway' if event['type'] == 'hold':
for event in events): if event['holdClimateRef'] == 'away':
# away hold is auto away or a temporary hold from away climate if int(event['endDate'][0:4]) - \
hold = 'away' int(event['startDate'][0:4]) <= 1:
elif any(event['holdClimateRef'] == 'away' and # a temporary hold from away climate is a hold
int(event['endDate'][0:4])-int(event['startDate'][0:4]) > 1 return 'away'
for event in events):
# a permanent away is not considered a hold, but away_mode
hold = None
elif any(event['holdClimateRef'] == 'home' or
event['type'] == 'autoHome'
for event in events):
# home mode is auto home or any home hold
hold = 'home'
elif any(event['type'] == 'hold' and event['running']
for event in events):
hold = 'temp'
# temperature hold is any other hold not based on climate
else: else:
hold = None # a premanent hold from away climate is away_mode
return hold return None
elif event['holdClimateRef'] != "":
# any other hold based on climate
return event['holdClimateRef']
else:
# any hold not based on a climate is a temp hold
return TEMPERATURE_HOLD
elif event['type'].startswith('auto'):
# all auto modes are treated as holds
return event['type'][4:].lower()
elif event['type'] == 'vacation':
self.vacation = event['name']
return VACATION_HOLD
return None
@property @property
def current_operation(self): def current_operation(self):
@ -232,8 +237,11 @@ class Thermostat(ClimateDevice):
@property @property
def mode(self): def mode(self):
"""Return current mode ie. home, away, sleep.""" """Return current mode, as the user-visible name."""
return self.thermostat['program']['currentClimateRef'] cur = self.thermostat['program']['currentClimateRef']
climates = self.thermostat['program']['climates']
current = list(filter(lambda x: x['climateRef'] == cur, climates))
return current[0]['name']
@property @property
def fan_min_on_time(self): def fan_min_on_time(self):
@ -261,51 +269,43 @@ class Thermostat(ClimateDevice):
"fan": self.fan, "fan": self.fan,
"mode": self.mode, "mode": self.mode,
"operation": operation, "operation": operation,
"climate_list": self.climate_list,
"fan_min_on_time": self.fan_min_on_time "fan_min_on_time": self.fan_min_on_time
} }
def is_vacation_on(self):
"""Return true if vacation mode is on."""
events = self.thermostat['events']
return any(event['type'] == 'vacation' and event['running']
for event in events)
@property @property
def is_away_mode_on(self): def is_away_mode_on(self):
"""Return true if away mode is on.""" """Return true if away mode is on."""
events = self.thermostat['events'] return self.current_hold_mode == 'away'
return any(event['holdClimateRef'] == 'away' and
int(event['endDate'][0:4])-int(event['startDate'][0:4]) > 1
for event in events)
def turn_away_mode_on(self): def turn_away_mode_on(self):
"""Turn away on.""" """Turn away on."""
self.data.ecobee.set_climate_hold(self.thermostat_index, self.set_hold_mode('away')
"away", 'indefinite')
self.update_without_throttle = True
def turn_away_mode_off(self): def turn_away_mode_off(self):
"""Turn away off.""" """Turn away off."""
self.data.ecobee.resume_program(self.thermostat_index) self.set_hold_mode(None)
self.update_without_throttle = True
def set_hold_mode(self, hold_mode): def set_hold_mode(self, hold_mode):
"""Set hold mode (away, home, temp).""" """Set hold mode (away, home, temp, sleep, etc.)."""
hold = self.current_hold_mode hold = self.current_hold_mode
if hold == hold_mode: if hold == hold_mode:
# no change, so no action required # no change, so no action required
return return
elif hold_mode == 'away': elif hold_mode == 'None' or hold_mode is None:
self.data.ecobee.set_climate_hold(self.thermostat_index, if hold == VACATION_HOLD:
"away", self.hold_preference()) self.data.ecobee.delete_vacation(self.thermostat_index,
elif hold_mode == 'home': self.vacation)
self.data.ecobee.set_climate_hold(self.thermostat_index,
"home", self.hold_preference())
elif hold_mode == 'temp':
self.set_temp_hold(int(self.current_temperature))
else: else:
self.data.ecobee.resume_program(self.thermostat_index) self.data.ecobee.resume_program(self.thermostat_index)
else:
if hold_mode == TEMPERATURE_HOLD:
self.set_temp_hold(int(self.current_temperature))
else:
self.data.ecobee.set_climate_hold(self.thermostat_index,
hold_mode,
self.hold_preference())
self.update_without_throttle = True self.update_without_throttle = True
def set_auto_temp_hold(self, heat_temp, cool_temp): def set_auto_temp_hold(self, heat_temp, cool_temp):
@ -382,3 +382,9 @@ class Thermostat(ClimateDevice):
# as an indefinite away hold is interpreted as away_mode # as an indefinite away hold is interpreted as away_mode
else: else:
return 'nextTransition' return 'nextTransition'
@property
def climate_list(self):
"""Return the list of climates currently available."""
climates = self.thermostat['program']['climates']
return list(map((lambda x: x['name']), climates))