Add ecobee indefinite away preset, remove unusable/broken presets (#108636)
* Add ecobee indefinite away preset, remove unusable/broken presets * Revert cleanup of presets which no longer work
This commit is contained in:
parent
d3c68303b0
commit
9e86f82a1b
6 changed files with 88 additions and 17 deletions
|
@ -12,7 +12,6 @@ from homeassistant.components.climate import (
|
|||
ATTR_TARGET_TEMP_LOW,
|
||||
FAN_AUTO,
|
||||
FAN_ON,
|
||||
PRESET_AWAY,
|
||||
PRESET_NONE,
|
||||
ClimateEntity,
|
||||
ClimateEntityFeature,
|
||||
|
@ -38,7 +37,7 @@ from homeassistant.util.unit_conversion import TemperatureConverter
|
|||
|
||||
from . import EcobeeData
|
||||
from .const import _LOGGER, DOMAIN, ECOBEE_MODEL_TO_NAME, MANUFACTURER
|
||||
from .util import ecobee_date, ecobee_time
|
||||
from .util import ecobee_date, ecobee_time, is_indefinite_hold
|
||||
|
||||
ATTR_COOL_TEMP = "cool_temp"
|
||||
ATTR_END_DATE = "end_date"
|
||||
|
@ -56,6 +55,7 @@ ATTR_AUTO_AWAY = "auto_away"
|
|||
ATTR_FOLLOW_ME = "follow_me"
|
||||
|
||||
DEFAULT_RESUME_ALL = False
|
||||
PRESET_AWAY_INDEFINITELY = "away_indefinitely"
|
||||
PRESET_TEMPERATURE = "temp"
|
||||
PRESET_VACATION = "vacation"
|
||||
PRESET_HOLD_NEXT_TRANSITION = "next_transition"
|
||||
|
@ -325,6 +325,7 @@ class Thermostat(ClimateEntity):
|
|||
_attr_name = None
|
||||
_attr_has_entity_name = True
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
_attr_translation_key = "ecobee"
|
||||
|
||||
def __init__(
|
||||
self, data: EcobeeData, thermostat_index: int, thermostat: dict
|
||||
|
@ -481,6 +482,11 @@ class Thermostat(ClimateEntity):
|
|||
continue
|
||||
|
||||
if event["type"] == "hold":
|
||||
if event["holdClimateRef"] == "away" and is_indefinite_hold(
|
||||
event["startDate"], event["endDate"]
|
||||
):
|
||||
return PRESET_AWAY_INDEFINITELY
|
||||
|
||||
if event["holdClimateRef"] in self._preset_modes:
|
||||
return self._preset_modes[event["holdClimateRef"]]
|
||||
|
||||
|
@ -577,7 +583,7 @@ class Thermostat(ClimateEntity):
|
|||
if self.preset_mode == PRESET_VACATION:
|
||||
self.data.ecobee.delete_vacation(self.thermostat_index, self.vacation)
|
||||
|
||||
if preset_mode == PRESET_AWAY:
|
||||
if preset_mode == PRESET_AWAY_INDEFINITELY:
|
||||
self.data.ecobee.set_climate_hold(
|
||||
self.thermostat_index, "away", "indefinite", self.hold_hours()
|
||||
)
|
||||
|
@ -625,7 +631,9 @@ class Thermostat(ClimateEntity):
|
|||
@property
|
||||
def preset_modes(self):
|
||||
"""Return available preset modes."""
|
||||
return list(self._preset_modes.values())
|
||||
# Return presets provided by the ecobee API, and an indefinite away
|
||||
# preset which we handle separately in set_preset_mode().
|
||||
return [*self._preset_modes.values(), PRESET_AWAY_INDEFINITELY]
|
||||
|
||||
def set_auto_temp_hold(self, heat_temp, cool_temp):
|
||||
"""Set temperature hold in auto mode."""
|
||||
|
|
|
@ -20,6 +20,17 @@
|
|||
}
|
||||
},
|
||||
"entity": {
|
||||
"climate": {
|
||||
"ecobee": {
|
||||
"state_attributes": {
|
||||
"preset_mode": {
|
||||
"state": {
|
||||
"away_indefinitely": "Away Indefinitely"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"number": {
|
||||
"ventilator_min_type_home": {
|
||||
"name": "Ventilator min time home"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
"""Validation utility functions for ecobee services."""
|
||||
|
||||
from datetime import datetime
|
||||
from datetime import date, datetime, timedelta
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
|
@ -23,3 +23,13 @@ def ecobee_time(time_string):
|
|||
"Time does not match ecobee 24-hour time format HH:MM:SS"
|
||||
) from err
|
||||
return time_string
|
||||
|
||||
|
||||
def is_indefinite_hold(start_date_string: str, end_date_string: str) -> bool:
|
||||
"""Determine if the given start and end dates from the ecobee API represent an indefinite hold.
|
||||
|
||||
This is not documented in the API, so a rough heuristic is used where a hold over 1 year is considered indefinite.
|
||||
"""
|
||||
return date.fromisoformat(end_date_string) - date.fromisoformat(
|
||||
start_date_string
|
||||
) > timedelta(days=365)
|
||||
|
|
|
@ -39,8 +39,10 @@ GENERIC_THERMOSTAT_INFO = {
|
|||
"running": True,
|
||||
"type": "hold",
|
||||
"holdClimateRef": "away",
|
||||
"endDate": "2022-01-01 10:00:00",
|
||||
"startDate": "2022-02-02 11:00:00",
|
||||
"startDate": "2022-02-02",
|
||||
"startTime": "11:00:00",
|
||||
"endDate": "2022-01-01",
|
||||
"endTime": "10:00:00",
|
||||
}
|
||||
],
|
||||
"remoteSensors": [
|
||||
|
@ -99,8 +101,10 @@ GENERIC_THERMOSTAT_INFO_WITH_HEATPUMP = {
|
|||
"running": True,
|
||||
"type": "hold",
|
||||
"holdClimateRef": "away",
|
||||
"endDate": "2022-01-01 10:00:00",
|
||||
"startDate": "2022-02-02 11:00:00",
|
||||
"startDate": "2022-02-02",
|
||||
"startTime": "11:00:00",
|
||||
"endDate": "2022-01-01",
|
||||
"endTime": "10:00:00",
|
||||
}
|
||||
],
|
||||
"remoteSensors": [
|
||||
|
|
|
@ -42,8 +42,10 @@
|
|||
"running": true,
|
||||
"type": "hold",
|
||||
"holdClimateRef": "away",
|
||||
"endDate": "2022-01-01 10:00:00",
|
||||
"startDate": "2022-02-02 11:00:00"
|
||||
"startDate": "2022-02-02",
|
||||
"startTime": "11:00:00",
|
||||
"endDate": "2022-01-01",
|
||||
"endTime": "10:00:00"
|
||||
}
|
||||
],
|
||||
"remoteSensors": [
|
||||
|
@ -110,8 +112,10 @@
|
|||
"running": true,
|
||||
"type": "hold",
|
||||
"holdClimateRef": "away",
|
||||
"endDate": "2022-01-01 10:00:00",
|
||||
"startDate": "2022-02-02 11:00:00"
|
||||
"startDate": "2022-02-02",
|
||||
"startTime": "11:00:00",
|
||||
"endDate": "2022-01-01",
|
||||
"endTime": "10:00:00"
|
||||
}
|
||||
],
|
||||
"remoteSensors": [
|
||||
|
|
|
@ -9,7 +9,11 @@ import pytest
|
|||
|
||||
from homeassistant.components import climate
|
||||
from homeassistant.components.climate import ClimateEntityFeature
|
||||
from homeassistant.components.ecobee.climate import ECOBEE_AUX_HEAT_ONLY, Thermostat
|
||||
from homeassistant.components.ecobee.climate import (
|
||||
ECOBEE_AUX_HEAT_ONLY,
|
||||
PRESET_AWAY_INDEFINITELY,
|
||||
Thermostat,
|
||||
)
|
||||
import homeassistant.const as const
|
||||
from homeassistant.const import ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, STATE_OFF
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
@ -31,6 +35,7 @@ def ecobee_fixture():
|
|||
"climates": [
|
||||
{"name": "Climate1", "climateRef": "c1"},
|
||||
{"name": "Climate2", "climateRef": "c2"},
|
||||
{"name": "Away", "climateRef": "away"},
|
||||
],
|
||||
"currentClimateRef": "c1",
|
||||
},
|
||||
|
@ -56,9 +61,11 @@ def ecobee_fixture():
|
|||
"name": "Event1",
|
||||
"running": True,
|
||||
"type": "hold",
|
||||
"holdClimateRef": "away",
|
||||
"endDate": "2017-01-01 10:00:00",
|
||||
"startDate": "2017-02-02 11:00:00",
|
||||
"holdClimateRef": "c1",
|
||||
"startDate": "2017-02-02",
|
||||
"startTime": "11:00:00",
|
||||
"endDate": "2017-01-01",
|
||||
"endTime": "10:00:00",
|
||||
}
|
||||
],
|
||||
}
|
||||
|
@ -428,3 +435,30 @@ async def test_turn_aux_heat_off(hass: HomeAssistant, mock_ecobee: MagicMock) ->
|
|||
)
|
||||
assert mock_ecobee.set_hvac_mode.call_count == 1
|
||||
assert mock_ecobee.set_hvac_mode.call_args == mock.call(0, "auto")
|
||||
|
||||
|
||||
async def test_preset_indefinite_away(ecobee_fixture, thermostat) -> None:
|
||||
"""Test indefinite away showing correctly, and not as temporary away."""
|
||||
ecobee_fixture["program"]["currentClimateRef"] = "away"
|
||||
ecobee_fixture["events"][0]["holdClimateRef"] = "away"
|
||||
assert thermostat.preset_mode == "Away"
|
||||
|
||||
ecobee_fixture["events"][0]["endDate"] = "2999-01-01"
|
||||
assert thermostat.preset_mode == PRESET_AWAY_INDEFINITELY
|
||||
|
||||
|
||||
async def test_set_preset_mode(ecobee_fixture, thermostat, data) -> None:
|
||||
"""Test set preset mode."""
|
||||
# Set a preset provided by ecobee.
|
||||
data.reset_mock()
|
||||
thermostat.set_preset_mode("Climate2")
|
||||
data.ecobee.set_climate_hold.assert_has_calls(
|
||||
[mock.call(1, "c2", thermostat.hold_preference(), thermostat.hold_hours())]
|
||||
)
|
||||
|
||||
# Set the indefinite away preset provided by this integration.
|
||||
data.reset_mock()
|
||||
thermostat.set_preset_mode(PRESET_AWAY_INDEFINITELY)
|
||||
data.ecobee.set_climate_hold.assert_has_calls(
|
||||
[mock.call(1, "away", "indefinite", thermostat.hold_hours())]
|
||||
)
|
||||
|
|
Loading…
Add table
Reference in a new issue