mirror of
https://github.com/home-assistant/core.git
synced 2025-07-13 16:27:08 +00:00
Add overlay options to Tado (#65886)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
This commit is contained in:
parent
09f6785956
commit
e76170fbfd
@ -1003,8 +1003,8 @@ build.json @home-assistant/supervisor
|
||||
/homeassistant/components/syslog/ @fabaff
|
||||
/homeassistant/components/system_bridge/ @timmo001
|
||||
/tests/components/system_bridge/ @timmo001
|
||||
/homeassistant/components/tado/ @michaelarnauts
|
||||
/tests/components/tado/ @michaelarnauts
|
||||
/homeassistant/components/tado/ @michaelarnauts @north3221
|
||||
/tests/components/tado/ @michaelarnauts @north3221
|
||||
/homeassistant/components/tag/ @balloob @dmulcahey
|
||||
/tests/components/tag/ @balloob @dmulcahey
|
||||
/homeassistant/components/tailscale/ @frenck
|
||||
|
@ -3,7 +3,6 @@ from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from PyTado.interface import Tado
|
||||
from PyTado.zone import TadoZone
|
||||
from requests import RequestException
|
||||
import requests.exceptions
|
||||
|
||||
@ -19,6 +18,7 @@ from homeassistant.util import Throttle
|
||||
|
||||
from .const import (
|
||||
CONF_FALLBACK,
|
||||
CONST_OVERLAY_TADO_MODE,
|
||||
DATA,
|
||||
DOMAIN,
|
||||
INSIDE_TEMPERATURE_MEASUREMENT,
|
||||
@ -51,7 +51,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
username = entry.data[CONF_USERNAME]
|
||||
password = entry.data[CONF_PASSWORD]
|
||||
fallback = entry.options.get(CONF_FALLBACK, True)
|
||||
fallback = entry.options.get(CONF_FALLBACK, CONST_OVERLAY_TADO_MODE)
|
||||
|
||||
tadoconnector = TadoConnector(hass, username, password, fallback)
|
||||
|
||||
@ -99,7 +99,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
def _async_import_options_from_data_if_missing(hass: HomeAssistant, entry: ConfigEntry):
|
||||
options = dict(entry.options)
|
||||
if CONF_FALLBACK not in options:
|
||||
options[CONF_FALLBACK] = entry.data.get(CONF_FALLBACK, True)
|
||||
options[CONF_FALLBACK] = entry.data.get(CONF_FALLBACK, CONST_OVERLAY_TADO_MODE)
|
||||
hass.config_entries.async_update_entry(entry, options=options)
|
||||
|
||||
|
||||
@ -213,23 +213,8 @@ class TadoConnector:
|
||||
_LOGGER.error("Unable to connect to Tado while updating zones")
|
||||
return
|
||||
|
||||
for zone in self.zones:
|
||||
zone_id = zone["id"]
|
||||
_LOGGER.debug("Updating zone %s", zone_id)
|
||||
zone_state = TadoZone(zone_states[str(zone_id)], zone_id)
|
||||
|
||||
self.data["zone"][zone_id] = zone_state
|
||||
|
||||
_LOGGER.debug(
|
||||
"Dispatching update to %s zone %s: %s",
|
||||
self.home_id,
|
||||
zone_id,
|
||||
zone_state,
|
||||
)
|
||||
dispatcher_send(
|
||||
self.hass,
|
||||
SIGNAL_TADO_UPDATE_RECEIVED.format(self.home_id, "zone", zone["id"]),
|
||||
)
|
||||
for zone in zone_states:
|
||||
self.update_zone(int(zone))
|
||||
|
||||
def update_zone(self, zone_id):
|
||||
"""Update the internal data from Tado."""
|
||||
|
@ -24,6 +24,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import (
|
||||
CONST_EXCLUSIVE_OVERLAY_GROUP,
|
||||
CONST_FAN_AUTO,
|
||||
CONST_FAN_OFF,
|
||||
CONST_MODE_AUTO,
|
||||
@ -32,10 +33,14 @@ from .const import (
|
||||
CONST_MODE_OFF,
|
||||
CONST_MODE_SMART_SCHEDULE,
|
||||
CONST_OVERLAY_MANUAL,
|
||||
CONST_OVERLAY_TADO_DEFAULT,
|
||||
CONST_OVERLAY_TADO_MODE,
|
||||
CONST_OVERLAY_TADO_OPTIONS,
|
||||
CONST_OVERLAY_TIMER,
|
||||
DATA,
|
||||
DOMAIN,
|
||||
HA_TERMINATION_DURATION,
|
||||
HA_TERMINATION_TYPE,
|
||||
HA_TO_TADO_FAN_MODE_MAP,
|
||||
HA_TO_TADO_HVAC_MODE_MAP,
|
||||
ORDERED_KNOWN_TADO_MODES,
|
||||
@ -58,12 +63,16 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SERVICE_CLIMATE_TIMER = "set_climate_timer"
|
||||
ATTR_TIME_PERIOD = "time_period"
|
||||
ATTR_REQUESTED_OVERLAY = "requested_overlay"
|
||||
|
||||
CLIMATE_TIMER_SCHEMA = {
|
||||
vol.Required(ATTR_TIME_PERIOD, default="01:00:00"): vol.All(
|
||||
vol.Required(ATTR_TEMPERATURE): vol.Coerce(float),
|
||||
vol.Exclusive(ATTR_TIME_PERIOD, CONST_EXCLUSIVE_OVERLAY_GROUP): vol.All(
|
||||
cv.time_period, cv.positive_timedelta, lambda td: td.total_seconds()
|
||||
),
|
||||
vol.Required(ATTR_TEMPERATURE): vol.Coerce(float),
|
||||
vol.Exclusive(ATTR_REQUESTED_OVERLAY, CONST_EXCLUSIVE_OVERLAY_GROUP): vol.In(
|
||||
CONST_OVERLAY_TADO_OPTIONS
|
||||
),
|
||||
}
|
||||
|
||||
SERVICE_TEMP_OFFSET = "set_climate_temperature_offset"
|
||||
@ -379,11 +388,14 @@ class TadoClimate(TadoZoneEntity, ClimateEntity):
|
||||
# the device is switching states
|
||||
return self._tado_zone_data.target_temp or self._tado_zone_data.current_temp
|
||||
|
||||
def set_timer(self, time_period, temperature=None):
|
||||
def set_timer(self, temperature=None, time_period=None, requested_overlay=None):
|
||||
"""Set the timer on the entity, and temperature if supported."""
|
||||
|
||||
self._control_hvac(
|
||||
hvac_mode=CONST_MODE_HEAT, target_temp=temperature, duration=time_period
|
||||
hvac_mode=CONST_MODE_HEAT,
|
||||
target_temp=temperature,
|
||||
duration=time_period,
|
||||
overlay_mode=requested_overlay,
|
||||
)
|
||||
|
||||
def set_temp_offset(self, offset):
|
||||
@ -464,7 +476,14 @@ class TadoClimate(TadoZoneEntity, ClimateEntity):
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
"""Return temperature offset."""
|
||||
return self._tado_zone_temp_offset
|
||||
state_attr = self._tado_zone_temp_offset
|
||||
state_attr[
|
||||
HA_TERMINATION_TYPE
|
||||
] = self._tado_zone_data.default_overlay_termination_type
|
||||
state_attr[
|
||||
HA_TERMINATION_DURATION
|
||||
] = self._tado_zone_data.default_overlay_termination_duration
|
||||
return state_attr
|
||||
|
||||
def set_swing_mode(self, swing_mode):
|
||||
"""Set swing modes for the device."""
|
||||
@ -474,6 +493,7 @@ class TadoClimate(TadoZoneEntity, ClimateEntity):
|
||||
def _async_update_zone_data(self):
|
||||
"""Load tado data into zone."""
|
||||
self._tado_zone_data = self._tado.data["zone"][self.zone_id]
|
||||
|
||||
# Assign offset values to mapped attributes
|
||||
for offset_key, attr in TADO_TO_HA_OFFSET_MAP.items():
|
||||
if (
|
||||
@ -518,6 +538,7 @@ class TadoClimate(TadoZoneEntity, ClimateEntity):
|
||||
fan_mode=None,
|
||||
swing_mode=None,
|
||||
duration=None,
|
||||
overlay_mode=None,
|
||||
):
|
||||
"""Send new target temperature to Tado."""
|
||||
|
||||
@ -559,22 +580,41 @@ class TadoClimate(TadoZoneEntity, ClimateEntity):
|
||||
self._tado.reset_zone_overlay(self.zone_id)
|
||||
return
|
||||
|
||||
# If user gave duration then overlay mode needs to be timer
|
||||
if duration:
|
||||
overlay_mode = CONST_OVERLAY_TIMER
|
||||
# If no duration or timer set to fallback setting
|
||||
if overlay_mode is None:
|
||||
overlay_mode = (
|
||||
self._tado.fallback
|
||||
if self._tado.fallback is not None
|
||||
else CONST_OVERLAY_TADO_MODE
|
||||
)
|
||||
# If default is Tado default then look it up
|
||||
if overlay_mode == CONST_OVERLAY_TADO_DEFAULT:
|
||||
overlay_mode = (
|
||||
self._tado_zone_data.default_overlay_termination_type
|
||||
if self._tado_zone_data.default_overlay_termination_type is not None
|
||||
else CONST_OVERLAY_TADO_MODE
|
||||
)
|
||||
# If we ended up with a timer but no duration, set a default duration
|
||||
if overlay_mode == CONST_OVERLAY_TIMER and duration is None:
|
||||
duration = (
|
||||
self._tado_zone_data.default_overlay_termination_duration
|
||||
if self._tado_zone_data.default_overlay_termination_duration is not None
|
||||
else "3600"
|
||||
)
|
||||
|
||||
_LOGGER.debug(
|
||||
"Switching to %s for zone %s (%d) with temperature %s °C and duration %s",
|
||||
"Switching to %s for zone %s (%d) with temperature %s °C and duration %s using overlay %s",
|
||||
self._current_tado_hvac_mode,
|
||||
self.zone_name,
|
||||
self.zone_id,
|
||||
self._target_temp,
|
||||
duration,
|
||||
overlay_mode,
|
||||
)
|
||||
|
||||
overlay_mode = CONST_OVERLAY_MANUAL
|
||||
if duration:
|
||||
overlay_mode = CONST_OVERLAY_TIMER
|
||||
elif self._tado.fallback:
|
||||
# Fallback to Smart Schedule at next Schedule switch if we have fallback enabled
|
||||
overlay_mode = CONST_OVERLAY_TADO_MODE
|
||||
|
||||
temperature_to_send = self._target_temp
|
||||
if self._current_tado_hvac_mode in TADO_MODES_WITH_NO_TEMP_SETTING:
|
||||
# A temperature cannot be passed with these modes
|
||||
|
@ -11,12 +11,15 @@ from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
|
||||
from .const import CONF_FALLBACK, DOMAIN, UNIQUE_ID
|
||||
from .const import CONF_FALLBACK, CONST_OVERLAY_TADO_OPTIONS, DOMAIN, UNIQUE_ID
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DATA_SCHEMA = vol.Schema(
|
||||
{vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str}
|
||||
{
|
||||
vol.Required(CONF_USERNAME): str,
|
||||
vol.Required(CONF_PASSWORD): str,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@ -122,9 +125,9 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
||||
|
||||
data_schema = vol.Schema(
|
||||
{
|
||||
vol.Required(
|
||||
vol.Optional(
|
||||
CONF_FALLBACK, default=self.config_entry.options.get(CONF_FALLBACK)
|
||||
): bool,
|
||||
): vol.In(CONST_OVERLAY_TADO_OPTIONS),
|
||||
}
|
||||
)
|
||||
return self.async_show_form(step_id="init", data_schema=data_schema)
|
||||
|
@ -95,6 +95,17 @@ CONST_OVERLAY_TADO_MODE = (
|
||||
)
|
||||
CONST_OVERLAY_MANUAL = "MANUAL" # the user has change the temperature or mode manually
|
||||
CONST_OVERLAY_TIMER = "TIMER" # the temperature will be reset after a timespan
|
||||
CONST_OVERLAY_TADO_DEFAULT = (
|
||||
"TADO_DEFAULT" # use the setting from tado zone itself (set in Tado app or webapp)
|
||||
)
|
||||
CONST_OVERLAY_TADO_OPTIONS = [
|
||||
CONST_OVERLAY_TADO_MODE,
|
||||
CONST_OVERLAY_MANUAL,
|
||||
CONST_OVERLAY_TADO_DEFAULT,
|
||||
]
|
||||
CONST_EXCLUSIVE_OVERLAY_GROUP = (
|
||||
"overlay_group" # Overlay group for set_climate_timer service
|
||||
)
|
||||
|
||||
|
||||
# Heat always comes first since we get the
|
||||
@ -180,3 +191,7 @@ TADO_TO_HA_OFFSET_MAP = {
|
||||
TADO_OFFSET_CELSIUS: HA_OFFSET_CELSIUS,
|
||||
TADO_OFFSET_FAHRENHEIT: HA_OFFSET_FAHRENHEIT,
|
||||
}
|
||||
|
||||
# Constants for Overlay Default settings
|
||||
HA_TERMINATION_TYPE = "default_overlay_type"
|
||||
HA_TERMINATION_DURATION = "default_overlay_seconds"
|
||||
|
@ -3,7 +3,7 @@
|
||||
"name": "Tado",
|
||||
"documentation": "https://www.home-assistant.io/integrations/tado",
|
||||
"requirements": ["python-tado==0.12.0"],
|
||||
"codeowners": ["@michaelarnauts"],
|
||||
"codeowners": ["@michaelarnauts", "@north3221"],
|
||||
"config_flow": true,
|
||||
"homekit": {
|
||||
"models": ["tado", "AC02"]
|
||||
|
@ -6,14 +6,6 @@ set_climate_timer:
|
||||
integration: tado
|
||||
domain: climate
|
||||
fields:
|
||||
time_period:
|
||||
name: Time period
|
||||
description: Set the time period for the boost.
|
||||
required: true
|
||||
example: "01:30:00"
|
||||
default: "01:00:00"
|
||||
selector:
|
||||
text:
|
||||
temperature:
|
||||
name: Temperature
|
||||
description: Temperature to set climate entity to
|
||||
@ -24,6 +16,26 @@ set_climate_timer:
|
||||
max: 100
|
||||
step: 0.5
|
||||
unit_of_measurement: '°'
|
||||
time_period:
|
||||
name: Time period
|
||||
description: Choose this or Overlay. Set the time period for the change if you want to be specific. Alternatively use Overlay
|
||||
required: false
|
||||
example: "01:30:00"
|
||||
default: "01:00:00"
|
||||
selector:
|
||||
text:
|
||||
requested_overlay:
|
||||
name: Overlay
|
||||
description: Choose this or Time Period. Allows you to choose an overlay. MANUAL:=Overlay until user removes; NEXT_TIME_BLOCK:=Overlay until next timeblock; TADO_DEFAULT:=Overlay based on tado app setting
|
||||
required: false
|
||||
example: "MANUAL"
|
||||
default: "TADO_DEFAULT"
|
||||
selector:
|
||||
select:
|
||||
options:
|
||||
- "NEXT_TIME_BLOCK"
|
||||
- "MANUAL"
|
||||
- "TADO_DEFAULT"
|
||||
|
||||
set_water_heater_timer:
|
||||
name: Set water heater timer
|
||||
|
@ -22,9 +22,9 @@
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"description": "Fallback mode will switch to Smart Schedule at next schedule switch after manually adjusting a zone.",
|
||||
"description": "Fallback mode lets you choose when to fallback to Smart Schedule from your manual zone overlay. (NEXT_TIME_BLOCK:= Change at next Smart Schedule change; MANUAL:= Dont change until you cancel; TADO_DEFAULT:= Change based on your setting in Tado App).",
|
||||
"data": {
|
||||
"fallback": "Enable fallback mode."
|
||||
"fallback": "Choose fallback mode."
|
||||
},
|
||||
"title": "Adjust Tado options."
|
||||
}
|
||||
|
@ -146,7 +146,9 @@ async def async_init_integration(
|
||||
text=load_fixture(zone_1_state_fixture),
|
||||
)
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN, data={CONF_USERNAME: "mock", CONF_PASSWORD: "mock"}
|
||||
domain=DOMAIN,
|
||||
data={CONF_USERNAME: "mock", CONF_PASSWORD: "mock"},
|
||||
options={"fallback": "NEXT_TIME_BLOCK"},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user