Revert "Breakout tado zone code into a single place (#32564)" (#32639)

This reverts commit c2b03332a0.
This commit is contained in:
Michaël Arnauts 2020-03-10 09:32:56 +01:00 committed by GitHub
parent ac9c9377c2
commit 8c52e2c923
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 410 additions and 1999 deletions

View file

@ -350,7 +350,7 @@ homeassistant/components/switchmate/* @danielhiversen
homeassistant/components/syncthru/* @nielstron
homeassistant/components/synology_srm/* @aerialls
homeassistant/components/syslog/* @fabaff
homeassistant/components/tado/* @michaelarnauts @bdraco
homeassistant/components/tado/* @michaelarnauts
homeassistant/components/tahoma/* @philklei
homeassistant/components/tankerkoenig/* @guillempages
homeassistant/components/tautulli/* @ludeeus

View file

@ -1,13 +1,12 @@
"""Support for the (unofficial) Tado API."""
from datetime import timedelta
import logging
import urllib
from PyTado.interface import Tado
from requests import RequestException
import voluptuous as vol
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.discovery import load_platform
from homeassistant.helpers.dispatcher import dispatcher_send
@ -110,7 +109,7 @@ class TadoConnector:
"""Connect to Tado and fetch the zones."""
try:
self.tado = Tado(self._username, self._password)
except (RuntimeError, RequestException) as exc:
except (RuntimeError, urllib.error.HTTPError) as exc:
_LOGGER.error("Unable to connect: %s", exc)
return False
@ -137,12 +136,7 @@ class TadoConnector:
if sensor_type == "zone":
data = self.tado.getState(sensor)
elif sensor_type == "device":
devices_data = self.tado.getDevices()
if not devices_data:
_LOGGER.info("There are no devices to setup on this tado account.")
return
data = devices_data[0]
data = self.tado.getDevices()[0]
else:
_LOGGER.debug("Unknown sensor: %s", sensor_type)
return
@ -168,62 +162,31 @@ class TadoConnector:
self.tado.resetZoneOverlay(zone_id)
self.update_sensor("zone", zone_id)
def set_home(self):
"""Put tado in home mode."""
response_json = None
try:
response_json = self.tado.setHome()
except RequestException as exc:
_LOGGER.error("Could not set home: %s", exc)
_raise_home_away_errors(response_json)
def set_away(self):
"""Put tado in away mode."""
response_json = None
try:
response_json = self.tado.setAway()
except RequestException as exc:
_LOGGER.error("Could not set away: %s", exc)
_raise_home_away_errors(response_json)
def set_zone_overlay(
self,
zone_id=None,
overlay_mode=None,
zone_id,
overlay_mode,
temperature=None,
duration=None,
device_type="HEATING",
mode=None,
fan_speed=None,
):
"""Set a zone overlay."""
_LOGGER.debug(
"Set overlay for zone %s: overlay_mode=%s, temp=%s, duration=%s, type=%s, mode=%s fan_speed=%s",
"Set overlay for zone %s: mode=%s, temp=%s, duration=%s, type=%s, mode=%s",
zone_id,
overlay_mode,
temperature,
duration,
device_type,
mode,
fan_speed,
)
try:
self.tado.setZoneOverlay(
zone_id,
overlay_mode,
temperature,
duration,
device_type,
"ON",
mode,
fan_speed,
zone_id, overlay_mode, temperature, duration, device_type, "ON", mode
)
except RequestException as exc:
_LOGGER.error("Could not set zone overlay: %s", exc)
except urllib.error.HTTPError as exc:
_LOGGER.error("Could not set zone overlay: %s", exc.read())
self.update_sensor("zone", zone_id)
@ -233,18 +196,7 @@ class TadoConnector:
self.tado.setZoneOverlay(
zone_id, overlay_mode, None, None, device_type, "OFF"
)
except RequestException as exc:
_LOGGER.error("Could not set zone overlay: %s", exc)
except urllib.error.HTTPError as exc:
_LOGGER.error("Could not set zone overlay: %s", exc.read())
self.update_sensor("zone", zone_id)
def _raise_home_away_errors(response_json):
if response_json is None:
return
# Likely we are displaying to the user:
# Tried to update to HOME though all mobile devices are detected outside the home fence
if "errors" in response_json and len(response_json["errors"]) > 0:
error_list = response_json["errors"]
raise HomeAssistantError(error_list[0]["title"])

View file

@ -3,12 +3,21 @@ import logging
from homeassistant.components.climate import ClimateDevice
from homeassistant.components.climate.const import (
CURRENT_HVAC_COOL,
CURRENT_HVAC_HEAT,
CURRENT_HVAC_IDLE,
CURRENT_HVAC_OFF,
FAN_AUTO,
FAN_HIGH,
FAN_LOW,
FAN_MIDDLE,
FAN_OFF,
HVAC_MODE_AUTO,
HVAC_MODE_COOL,
HVAC_MODE_HEAT,
HVAC_MODE_HEAT_COOL,
HVAC_MODE_OFF,
PRESET_AWAY,
PRESET_HOME,
SUPPORT_FAN_MODE,
SUPPORT_PRESET_MODE,
SUPPORT_TARGET_TEMPERATURE,
)
@ -18,29 +27,49 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect
from . import DOMAIN, SIGNAL_TADO_UPDATE_RECEIVED
from .const import (
CONST_FAN_AUTO,
CONST_FAN_OFF,
CONST_MODE_COOL,
CONST_MODE_HEAT,
CONST_MODE_OFF,
CONST_MODE_SMART_SCHEDULE,
CONST_OVERLAY_MANUAL,
CONST_OVERLAY_TADO_MODE,
CONST_OVERLAY_TIMER,
DATA,
HA_TO_TADO_FAN_MODE_MAP,
HA_TO_TADO_HVAC_MODE_MAP,
ORDERED_KNOWN_TADO_MODES,
SUPPORT_PRESET,
TADO_MODES_WITH_NO_TEMP_SETTING,
TADO_TO_HA_FAN_MODE_MAP,
TADO_TO_HA_HVAC_MODE_MAP,
TYPE_AIR_CONDITIONING,
TYPE_HEATING,
)
from .tado_adapter import TadoZoneData
_LOGGER = logging.getLogger(__name__)
FAN_MAP_TADO = {"HIGH": FAN_HIGH, "MIDDLE": FAN_MIDDLE, "LOW": FAN_LOW}
HVAC_MAP_TADO_HEAT = {
CONST_OVERLAY_MANUAL: HVAC_MODE_HEAT,
CONST_OVERLAY_TIMER: HVAC_MODE_HEAT,
CONST_OVERLAY_TADO_MODE: HVAC_MODE_HEAT,
CONST_MODE_SMART_SCHEDULE: HVAC_MODE_AUTO,
CONST_MODE_OFF: HVAC_MODE_OFF,
}
HVAC_MAP_TADO_COOL = {
CONST_OVERLAY_MANUAL: HVAC_MODE_COOL,
CONST_OVERLAY_TIMER: HVAC_MODE_COOL,
CONST_OVERLAY_TADO_MODE: HVAC_MODE_COOL,
CONST_MODE_SMART_SCHEDULE: HVAC_MODE_AUTO,
CONST_MODE_OFF: HVAC_MODE_OFF,
}
HVAC_MAP_TADO_HEAT_COOL = {
CONST_OVERLAY_MANUAL: HVAC_MODE_HEAT_COOL,
CONST_OVERLAY_TIMER: HVAC_MODE_HEAT_COOL,
CONST_OVERLAY_TADO_MODE: HVAC_MODE_HEAT_COOL,
CONST_MODE_SMART_SCHEDULE: HVAC_MODE_AUTO,
CONST_MODE_OFF: HVAC_MODE_OFF,
}
SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_PRESET_MODE
SUPPORT_HVAC_HEAT = [HVAC_MODE_HEAT, HVAC_MODE_AUTO, HVAC_MODE_OFF]
SUPPORT_HVAC_COOL = [HVAC_MODE_COOL, HVAC_MODE_AUTO, HVAC_MODE_OFF]
SUPPORT_HVAC_HEAT_COOL = [HVAC_MODE_HEAT_COOL, HVAC_MODE_AUTO, HVAC_MODE_OFF]
SUPPORT_FAN = [FAN_HIGH, FAN_MIDDLE, FAN_LOW, FAN_OFF]
SUPPORT_PRESET = [PRESET_AWAY, PRESET_HOME]
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the Tado climate platform."""
@ -67,80 +96,29 @@ def create_climate_entity(tado, name: str, zone_id: int):
_LOGGER.debug("Capabilities for zone %s: %s", zone_id, capabilities)
zone_type = capabilities["type"]
support_flags = SUPPORT_PRESET_MODE | SUPPORT_TARGET_TEMPERATURE
supported_hvac_modes = [
TADO_TO_HA_HVAC_MODE_MAP[CONST_MODE_OFF],
TADO_TO_HA_HVAC_MODE_MAP[CONST_MODE_SMART_SCHEDULE],
]
supported_fan_modes = None
heat_temperatures = None
cool_temperatures = None
ac_support_heat = False
if zone_type == TYPE_AIR_CONDITIONING:
# Only use heat if available
# (you don't have to setup a heat mode, but cool is required)
# Heat is preferred as it generally has a lower minimum temperature
for mode in ORDERED_KNOWN_TADO_MODES:
if mode not in capabilities:
continue
supported_hvac_modes.append(TADO_TO_HA_HVAC_MODE_MAP[mode])
if not capabilities[mode].get("fanSpeeds"):
continue
support_flags |= SUPPORT_FAN_MODE
if supported_fan_modes:
continue
supported_fan_modes = [
TADO_TO_HA_FAN_MODE_MAP[speed]
for speed in capabilities[mode]["fanSpeeds"]
]
cool_temperatures = capabilities[CONST_MODE_COOL]["temperatures"]
if "HEAT" in capabilities:
temperatures = capabilities["HEAT"]["temperatures"]
ac_support_heat = True
else:
temperatures = capabilities["COOL"]["temperatures"]
elif "temperatures" in capabilities:
temperatures = capabilities["temperatures"]
else:
supported_hvac_modes.append(HVAC_MODE_HEAT)
if CONST_MODE_HEAT in capabilities:
heat_temperatures = capabilities[CONST_MODE_HEAT]["temperatures"]
if heat_temperatures is None and "temperatures" in capabilities:
heat_temperatures = capabilities["temperatures"]
if cool_temperatures is None and heat_temperatures is None:
_LOGGER.debug("Not adding zone %s since it has no temperatures", name)
_LOGGER.debug("Not adding zone %s since it has no temperature", name)
return None
heat_min_temp = None
heat_max_temp = None
heat_step = None
cool_min_temp = None
cool_max_temp = None
cool_step = None
if heat_temperatures is not None:
heat_min_temp = float(heat_temperatures["celsius"]["min"])
heat_max_temp = float(heat_temperatures["celsius"]["max"])
heat_step = heat_temperatures["celsius"].get("step", PRECISION_TENTHS)
if cool_temperatures is not None:
cool_min_temp = float(cool_temperatures["celsius"]["min"])
cool_max_temp = float(cool_temperatures["celsius"]["max"])
cool_step = cool_temperatures["celsius"].get("step", PRECISION_TENTHS)
min_temp = float(temperatures["celsius"]["min"])
max_temp = float(temperatures["celsius"]["max"])
step = temperatures["celsius"].get("step", PRECISION_TENTHS)
entity = TadoClimate(
tado,
name,
zone_id,
zone_type,
heat_min_temp,
heat_max_temp,
heat_step,
cool_min_temp,
cool_max_temp,
cool_step,
supported_hvac_modes,
supported_fan_modes,
support_flags,
tado, name, zone_id, zone_type, min_temp, max_temp, step, ac_support_heat,
)
return entity
@ -154,15 +132,10 @@ class TadoClimate(ClimateDevice):
zone_name,
zone_id,
zone_type,
heat_min_temp,
heat_max_temp,
heat_step,
cool_min_temp,
cool_max_temp,
cool_step,
supported_hvac_modes,
supported_fan_modes,
support_flags,
min_temp,
max_temp,
step,
ac_support_heat,
):
"""Initialize of Tado climate entity."""
self._tado = tado
@ -173,45 +146,49 @@ class TadoClimate(ClimateDevice):
self._unique_id = f"{zone_type} {zone_id} {tado.device_id}"
self._ac_device = zone_type == TYPE_AIR_CONDITIONING
self._supported_hvac_modes = supported_hvac_modes
self._supported_fan_modes = supported_fan_modes
self._support_flags = support_flags
self._ac_support_heat = ac_support_heat
self._cooling = False
self._available = False
self._active = False
self._device_is_active = False
self._cur_temp = None
self._cur_humidity = None
self._heat_min_temp = heat_min_temp
self._heat_max_temp = heat_max_temp
self._heat_step = heat_step
self._cool_min_temp = cool_min_temp
self._cool_max_temp = cool_max_temp
self._cool_step = cool_step
self._is_away = False
self._min_temp = min_temp
self._max_temp = max_temp
self._step = step
self._target_temp = None
self._current_tado_fan_speed = CONST_FAN_OFF
self._current_tado_hvac_mode = CONST_MODE_OFF
self._current_hvac_action = CURRENT_HVAC_OFF
if tado.fallback:
# Fallback to Smart Schedule at next Schedule switch
self._default_overlay = CONST_OVERLAY_TADO_MODE
else:
# Don't fallback to Smart Schedule, but keep in manual mode
self._default_overlay = CONST_OVERLAY_MANUAL
self._tado_zone_data = None
self._async_update_zone_data()
self._current_fan = CONST_MODE_OFF
self._current_operation = CONST_MODE_SMART_SCHEDULE
self._overlay_mode = CONST_MODE_SMART_SCHEDULE
async def async_added_to_hass(self):
"""Register for sensor updates."""
@callback
def async_update_callback():
"""Schedule an entity update."""
self.async_schedule_update_ha_state(True)
async_dispatcher_connect(
self.hass,
SIGNAL_TADO_UPDATE_RECEIVED.format("zone", self.zone_id),
self._async_update_callback,
async_update_callback,
)
@property
def supported_features(self):
"""Return the list of supported features."""
return self._support_flags
return SUPPORT_FLAGS
@property
def name(self):
@ -231,12 +208,12 @@ class TadoClimate(ClimateDevice):
@property
def current_humidity(self):
"""Return the current humidity."""
return self._tado_zone_data.current_humidity
return self._cur_humidity
@property
def current_temperature(self):
"""Return the sensor temperature."""
return self._tado_zone_data.current_temp
return self._cur_temp
@property
def hvac_mode(self):
@ -244,9 +221,11 @@ class TadoClimate(ClimateDevice):
Need to be one of HVAC_MODE_*.
"""
return TADO_TO_HA_HVAC_MODE_MAP.get(
self._tado_zone_data.current_tado_hvac_mode, CURRENT_HVAC_OFF
)
if self._ac_device and self._ac_support_heat:
return HVAC_MAP_TADO_HEAT_COOL.get(self._current_operation)
if self._ac_device and not self._ac_support_heat:
return HVAC_MAP_TADO_COOL.get(self._current_operation)
return HVAC_MAP_TADO_HEAT.get(self._current_operation)
@property
def hvac_modes(self):
@ -254,7 +233,11 @@ class TadoClimate(ClimateDevice):
Need to be a subset of HVAC_MODES.
"""
return self._supported_hvac_modes
if self._ac_device:
if self._ac_support_heat:
return SUPPORT_HVAC_HEAT_COOL
return SUPPORT_HVAC_COOL
return SUPPORT_HVAC_HEAT
@property
def hvac_action(self):
@ -262,28 +245,40 @@ class TadoClimate(ClimateDevice):
Need to be one of CURRENT_HVAC_*.
"""
return self._tado_zone_data.current_hvac_action
if not self._device_is_active:
return CURRENT_HVAC_OFF
if self._ac_device:
if self._active:
if self._ac_support_heat and not self._cooling:
return CURRENT_HVAC_HEAT
return CURRENT_HVAC_COOL
return CURRENT_HVAC_IDLE
if self._active:
return CURRENT_HVAC_HEAT
return CURRENT_HVAC_IDLE
@property
def fan_mode(self):
"""Return the fan setting."""
if self._ac_device:
return TADO_TO_HA_FAN_MODE_MAP.get(self._current_tado_fan_speed, FAN_AUTO)
return FAN_MAP_TADO.get(self._current_fan)
return None
@property
def fan_modes(self):
"""List of available fan modes."""
return self._supported_fan_modes
if self._ac_device:
return SUPPORT_FAN
return None
def set_fan_mode(self, fan_mode: str):
"""Turn fan on/off."""
self._control_hvac(fan_mode=HA_TO_TADO_FAN_MODE_MAP[fan_mode])
pass
@property
def preset_mode(self):
"""Return the current preset mode (home, away)."""
if self._tado_zone_data.is_away:
if self._is_away:
return PRESET_AWAY
return PRESET_HOME
@ -294,10 +289,7 @@ class TadoClimate(ClimateDevice):
def set_preset_mode(self, preset_mode):
"""Set new preset mode."""
if preset_mode == PRESET_HOME:
self._tado.set_home()
else:
self._tado.set_away()
pass
@property
def temperature_unit(self):
@ -307,14 +299,12 @@ class TadoClimate(ClimateDevice):
@property
def target_temperature_step(self):
"""Return the supported step of target temperature."""
if self._tado_zone_data.current_tado_hvac_mode == CONST_MODE_COOL:
return self._cool_step or self._heat_step
return self._heat_step or self._cool_step
return self._step
@property
def target_temperature(self):
"""Return the temperature we try to reach."""
return self._tado_zone_data.target_temp
return self._target_temp
def set_temperature(self, **kwargs):
"""Set new target temperature."""
@ -322,142 +312,174 @@ class TadoClimate(ClimateDevice):
if temperature is None:
return
self._control_hvac(target_temp=temperature)
self._current_operation = self._default_overlay
self._overlay_mode = None
self._target_temp = temperature
self._control_heating()
def set_hvac_mode(self, hvac_mode):
"""Set new target hvac mode."""
mode = None
self._control_hvac(hvac_mode=HA_TO_TADO_HVAC_MODE_MAP[hvac_mode])
if hvac_mode == HVAC_MODE_OFF:
mode = CONST_MODE_OFF
elif hvac_mode == HVAC_MODE_AUTO:
mode = CONST_MODE_SMART_SCHEDULE
elif hvac_mode == HVAC_MODE_HEAT:
mode = self._default_overlay
elif hvac_mode == HVAC_MODE_COOL:
mode = self._default_overlay
elif hvac_mode == HVAC_MODE_HEAT_COOL:
mode = self._default_overlay
@property
def available(self):
"""Return if the device is available."""
return self._tado_zone_data.available
self._current_operation = mode
self._overlay_mode = None
# Set a target temperature if we don't have any
# This can happen when we switch from Off to On
if self._target_temp is None:
if self._ac_device:
self._target_temp = self.max_temp
else:
self._target_temp = self.min_temp
self.schedule_update_ha_state()
self._control_heating()
@property
def min_temp(self):
"""Return the minimum temperature."""
if (
self._current_tado_hvac_mode == CONST_MODE_COOL
and self._cool_min_temp is not None
):
return self._cool_min_temp
if self._heat_min_temp is not None:
return self._heat_min_temp
return self._cool_min_temp
return self._min_temp
@property
def max_temp(self):
"""Return the maximum temperature."""
return self._max_temp
def update(self):
"""Handle update callbacks."""
_LOGGER.debug("Updating climate platform for zone %d", self.zone_id)
data = self._tado.data["zone"][self.zone_id]
if "sensorDataPoints" in data:
sensor_data = data["sensorDataPoints"]
if "insideTemperature" in sensor_data:
temperature = float(sensor_data["insideTemperature"]["celsius"])
self._cur_temp = temperature
if "humidity" in sensor_data:
humidity = float(sensor_data["humidity"]["percentage"])
self._cur_humidity = humidity
# temperature setting will not exist when device is off
if (
self._current_tado_hvac_mode == CONST_MODE_HEAT
and self._heat_max_temp is not None
"temperature" in data["setting"]
and data["setting"]["temperature"] is not None
):
return self._heat_max_temp
if self._heat_max_temp is not None:
return self._heat_max_temp
setting = float(data["setting"]["temperature"]["celsius"])
self._target_temp = setting
return self._heat_max_temp
if "tadoMode" in data:
mode = data["tadoMode"]
self._is_away = mode == "AWAY"
@callback
def _async_update_zone_data(self):
"""Load tado data into zone."""
self._tado_zone_data = TadoZoneData(
self._tado.data["zone"][self.zone_id], self.zone_id
)
@callback
def _async_update_callback(self):
"""Load tado data and update state."""
self._async_update_zone_data()
self.async_write_ha_state()
def _normalize_target_temp_for_hvac_mode(self):
# Set a target temperature if we don't have any
# This can happen when we switch from Off to On
if self._target_temp is None:
if self._current_tado_hvac_mode == CONST_MODE_COOL:
self._target_temp = self._cool_max_temp
if "setting" in data:
power = data["setting"]["power"]
if power == "OFF":
self._current_operation = CONST_MODE_OFF
self._current_fan = CONST_MODE_OFF
# There is no overlay, the mode will always be
# "SMART_SCHEDULE"
self._overlay_mode = CONST_MODE_SMART_SCHEDULE
self._device_is_active = False
else:
self._target_temp = self._heat_min_temp
elif self._current_tado_hvac_mode == CONST_MODE_COOL:
if self._target_temp > self._cool_max_temp:
self._target_temp = self._cool_max_temp
elif self._target_temp < self._cool_min_temp:
self._target_temp = self._cool_min_temp
elif self._current_tado_hvac_mode == CONST_MODE_HEAT:
if self._target_temp > self._heat_max_temp:
self._target_temp = self._heat_max_temp
elif self._target_temp < self._heat_min_temp:
self._target_temp = self._heat_min_temp
self._device_is_active = True
def _control_hvac(self, hvac_mode=None, target_temp=None, fan_mode=None):
active = False
if "activityDataPoints" in data:
activity_data = data["activityDataPoints"]
if self._ac_device:
if "acPower" in activity_data and activity_data["acPower"] is not None:
if not activity_data["acPower"]["value"] == "OFF":
active = True
else:
if (
"heatingPower" in activity_data
and activity_data["heatingPower"] is not None
):
if float(activity_data["heatingPower"]["percentage"]) > 0.0:
active = True
self._active = active
overlay = False
overlay_data = None
termination = CONST_MODE_SMART_SCHEDULE
cooling = False
fan_speed = CONST_MODE_OFF
if "overlay" in data:
overlay_data = data["overlay"]
overlay = overlay_data is not None
if overlay:
termination = overlay_data["termination"]["type"]
setting = False
setting_data = None
if "setting" in overlay_data:
setting_data = overlay_data["setting"]
setting = setting_data is not None
if setting:
if "mode" in setting_data:
cooling = setting_data["mode"] == "COOL"
if "fanSpeed" in setting_data:
fan_speed = setting_data["fanSpeed"]
if self._device_is_active:
# If you set mode manually to off, there will be an overlay
# and a termination, but we want to see the mode "OFF"
self._overlay_mode = termination
self._current_operation = termination
self._cooling = cooling
self._current_fan = fan_speed
def _control_heating(self):
"""Send new target temperature to Tado."""
if hvac_mode:
self._current_tado_hvac_mode = hvac_mode
if target_temp:
self._target_temp = target_temp
if fan_mode:
self._current_tado_fan_speed = fan_mode
self._normalize_target_temp_for_hvac_mode()
# tado does not permit setting the fan speed to
# off, you must turn off the device
if (
self._current_tado_fan_speed == CONST_FAN_OFF
and self._current_tado_hvac_mode != CONST_MODE_OFF
):
self._current_tado_fan_speed = CONST_FAN_AUTO
if self._current_tado_hvac_mode == CONST_MODE_OFF:
_LOGGER.debug(
"Switching to OFF for zone %s (%d)", self.zone_name, self.zone_id
)
self._tado.set_zone_off(self.zone_id, CONST_OVERLAY_MANUAL, self.zone_type)
return
if self._current_tado_hvac_mode == CONST_MODE_SMART_SCHEDULE:
if self._current_operation == CONST_MODE_SMART_SCHEDULE:
_LOGGER.debug(
"Switching to SMART_SCHEDULE for zone %s (%d)",
self.zone_name,
self.zone_id,
)
self._tado.reset_zone_overlay(self.zone_id)
self._overlay_mode = self._current_operation
return
if self._current_operation == CONST_MODE_OFF:
_LOGGER.debug(
"Switching to OFF for zone %s (%d)", self.zone_name, self.zone_id
)
self._tado.set_zone_off(self.zone_id, CONST_OVERLAY_MANUAL, self.zone_type)
self._overlay_mode = self._current_operation
return
_LOGGER.debug(
"Switching to %s for zone %s (%d) with temperature %s °C",
self._current_tado_hvac_mode,
self._current_operation,
self.zone_name,
self.zone_id,
self._target_temp,
)
# Fallback to Smart Schedule at next Schedule switch if we have fallback enabled
overlay_mode = (
CONST_OVERLAY_TADO_MODE if self._tado.fallback else CONST_OVERLAY_MANUAL
)
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
temperature_to_send = None
self._tado.set_zone_overlay(
zone_id=self.zone_id,
overlay_mode=overlay_mode, # What to do when the period ends
temperature=temperature_to_send,
duration=None,
device_type=self.zone_type,
mode=self._current_tado_hvac_mode,
fan_speed=(
self._current_tado_fan_speed
if (self._support_flags & SUPPORT_FAN_MODE)
else None
), # api defaults to not sending fanSpeed if not specified
self.zone_id,
self._current_operation,
self._target_temp,
None,
self.zone_type,
"COOL" if self._ac_device else None,
)
self._overlay_mode = self._current_operation

View file

@ -1,26 +1,5 @@
"""Constant values for the Tado component."""
from homeassistant.components.climate.const import (
CURRENT_HVAC_COOL,
CURRENT_HVAC_DRY,
CURRENT_HVAC_FAN,
CURRENT_HVAC_HEAT,
FAN_AUTO,
FAN_HIGH,
FAN_LOW,
FAN_MEDIUM,
FAN_OFF,
HVAC_MODE_AUTO,
HVAC_MODE_COOL,
HVAC_MODE_DRY,
HVAC_MODE_FAN_ONLY,
HVAC_MODE_HEAT,
HVAC_MODE_HEAT_COOL,
HVAC_MODE_OFF,
PRESET_AWAY,
PRESET_HOME,
)
# Configuration
CONF_FALLBACK = "fallback"
DATA = "data"
@ -31,81 +10,10 @@ TYPE_HEATING = "HEATING"
TYPE_HOT_WATER = "HOT_WATER"
# Base modes
CONST_MODE_OFF = "OFF"
CONST_MODE_SMART_SCHEDULE = "SMART_SCHEDULE" # Use the schedule
CONST_MODE_AUTO = "AUTO"
CONST_MODE_COOL = "COOL"
CONST_MODE_HEAT = "HEAT"
CONST_MODE_DRY = "DRY"
CONST_MODE_FAN = "FAN"
CONST_LINK_OFFLINE = "OFFLINE"
CONST_FAN_OFF = "OFF"
CONST_FAN_AUTO = "AUTO"
CONST_FAN_LOW = "LOW"
CONST_FAN_MIDDLE = "MIDDLE"
CONST_FAN_HIGH = "HIGH"
CONST_MODE_OFF = "OFF" # Switch off heating in a zone
# When we change the temperature setting, we need an overlay mode
CONST_OVERLAY_TADO_MODE = "TADO_MODE" # wait until tado changes the mode automatic
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
# Heat always comes first since we get the
# min and max tempatures for the zone from
# it.
# Heat is preferred as it generally has a lower minimum temperature
ORDERED_KNOWN_TADO_MODES = [
CONST_MODE_HEAT,
CONST_MODE_COOL,
CONST_MODE_AUTO,
CONST_MODE_DRY,
CONST_MODE_FAN,
]
TADO_MODES_TO_HA_CURRENT_HVAC_ACTION = {
CONST_MODE_HEAT: CURRENT_HVAC_HEAT,
CONST_MODE_DRY: CURRENT_HVAC_DRY,
CONST_MODE_FAN: CURRENT_HVAC_FAN,
CONST_MODE_COOL: CURRENT_HVAC_COOL,
}
# These modes will not allow a temp to be set
TADO_MODES_WITH_NO_TEMP_SETTING = [CONST_MODE_AUTO, CONST_MODE_DRY, CONST_MODE_FAN]
#
# HVAC_MODE_HEAT_COOL is mapped to CONST_MODE_AUTO
# This lets tado decide on a temp
#
# HVAC_MODE_AUTO is mapped to CONST_MODE_SMART_SCHEDULE
# This runs the smart schedule
#
HA_TO_TADO_HVAC_MODE_MAP = {
HVAC_MODE_OFF: CONST_MODE_OFF,
HVAC_MODE_HEAT_COOL: CONST_MODE_AUTO,
HVAC_MODE_AUTO: CONST_MODE_SMART_SCHEDULE,
HVAC_MODE_HEAT: CONST_MODE_HEAT,
HVAC_MODE_COOL: CONST_MODE_COOL,
HVAC_MODE_DRY: CONST_MODE_DRY,
HVAC_MODE_FAN_ONLY: CONST_MODE_FAN,
}
HA_TO_TADO_FAN_MODE_MAP = {
FAN_AUTO: CONST_FAN_AUTO,
FAN_OFF: CONST_FAN_OFF,
FAN_LOW: CONST_FAN_LOW,
FAN_MEDIUM: CONST_FAN_MIDDLE,
FAN_HIGH: CONST_FAN_HIGH,
}
TADO_TO_HA_HVAC_MODE_MAP = {
value: key for key, value in HA_TO_TADO_HVAC_MODE_MAP.items()
}
TADO_TO_HA_FAN_MODE_MAP = {value: key for key, value in HA_TO_TADO_FAN_MODE_MAP.items()}
DEFAULT_TADO_PRECISION = 0.1
SUPPORT_PRESET = [PRESET_AWAY, PRESET_HOME]

View file

@ -3,10 +3,10 @@
"name": "Tado",
"documentation": "https://www.home-assistant.io/integrations/tado",
"requirements": [
"python-tado==0.4.0"
"python-tado==0.3.0"
],
"dependencies": [],
"codeowners": [
"@michaelarnauts", "@bdraco"
"@michaelarnauts"
]
}

View file

@ -8,7 +8,6 @@ from homeassistant.helpers.entity import Entity
from . import DATA, DOMAIN, SIGNAL_TADO_UPDATE_RECEIVED
from .const import TYPE_AIR_CONDITIONING, TYPE_HEATING, TYPE_HOT_WATER
from .tado_adapter import TadoZoneData
_LOGGER = logging.getLogger(__name__)
@ -51,7 +50,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
for zone in tado.zones:
entities.extend(
[
create_zone_sensor(hass, tado, zone["name"], zone["id"], variable)
create_zone_sensor(tado, zone["name"], zone["id"], variable)
for variable in ZONE_SENSORS.get(zone["type"])
]
)
@ -60,7 +59,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
for home in tado.devices:
entities.extend(
[
create_device_sensor(hass, tado, home["name"], home["id"], variable)
create_device_sensor(tado, home["name"], home["id"], variable)
for variable in DEVICE_SENSORS
]
)
@ -68,22 +67,21 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
add_entities(entities, True)
def create_zone_sensor(hass, tado, name, zone_id, variable):
def create_zone_sensor(tado, name, zone_id, variable):
"""Create a zone sensor."""
return TadoSensor(hass, tado, name, "zone", zone_id, variable)
return TadoSensor(tado, name, "zone", zone_id, variable)
def create_device_sensor(hass, tado, name, device_id, variable):
def create_device_sensor(tado, name, device_id, variable):
"""Create a device sensor."""
return TadoSensor(hass, tado, name, "device", device_id, variable)
return TadoSensor(tado, name, "device", device_id, variable)
class TadoSensor(Entity):
"""Representation of a tado Sensor."""
def __init__(self, hass, tado, zone_name, sensor_type, zone_id, zone_variable):
def __init__(self, tado, zone_name, sensor_type, zone_id, zone_variable):
"""Initialize of the Tado Sensor."""
self.hass = hass
self._tado = tado
self.zone_name = zone_name
@ -95,16 +93,19 @@ class TadoSensor(Entity):
self._state = None
self._state_attributes = None
self._tado_zone_data = None
self._async_update_zone_data()
async def async_added_to_hass(self):
"""Register for sensor updates."""
@callback
def async_update_callback():
"""Schedule an entity update."""
self.async_schedule_update_ha_state(True)
async_dispatcher_connect(
self.hass,
SIGNAL_TADO_UPDATE_RECEIVED.format(self.sensor_type, self.zone_id),
self._async_update_callback,
async_update_callback,
)
@property
@ -148,74 +149,97 @@ class TadoSensor(Entity):
return "mdi:water-percent"
@property
def should_poll(self):
def should_poll(self) -> bool:
"""Do not poll."""
return False
@callback
def _async_update_callback(self):
"""Update and write state."""
self._async_update_zone_data()
self.async_write_ha_state()
@callback
def _async_update_zone_data(self):
def update(self):
"""Handle update callbacks."""
try:
data = self._tado.data[self.sensor_type][self.zone_id]
except KeyError:
return
self._tado_zone_data = TadoZoneData(data, self.zone_id)
unit = TEMP_CELSIUS
if self.zone_variable == "temperature":
self._state = self.hass.config.units.temperature(
self._tado_zone_data.current_temp, TEMP_CELSIUS
)
self._state_attributes = {
"time": self._tado_zone_data.current_temp_timestamp,
"setting": 0, # setting is used in climate device
}
if "sensorDataPoints" in data:
sensor_data = data["sensorDataPoints"]
temperature = float(sensor_data["insideTemperature"]["celsius"])
self._state = self.hass.config.units.temperature(temperature, unit)
self._state_attributes = {
"time": sensor_data["insideTemperature"]["timestamp"],
"setting": 0, # setting is used in climate device
}
# temperature setting will not exist when device is off
if (
"temperature" in data["setting"]
and data["setting"]["temperature"] is not None
):
temperature = float(data["setting"]["temperature"]["celsius"])
self._state_attributes[
"setting"
] = self.hass.config.units.temperature(temperature, unit)
elif self.zone_variable == "humidity":
self._state = self._tado_zone_data.current_humidity
self._state_attributes = {
"time": self._tado_zone_data.current_humidity_timestamp
}
if "sensorDataPoints" in data:
sensor_data = data["sensorDataPoints"]
self._state = float(sensor_data["humidity"]["percentage"])
self._state_attributes = {"time": sensor_data["humidity"]["timestamp"]}
elif self.zone_variable == "power":
self._state = self._tado_zone_data.power
if "setting" in data:
self._state = data["setting"]["power"]
elif self.zone_variable == "link":
self._state = self._tado_zone_data.link
if "link" in data:
self._state = data["link"]["state"]
elif self.zone_variable == "heating":
self._state = self._tado_zone_data.heating_power_percentage
self._state_attributes = {
"time": self._tado_zone_data.heating_power_timestamp
}
if "activityDataPoints" in data:
activity_data = data["activityDataPoints"]
if (
"heatingPower" in activity_data
and activity_data["heatingPower"] is not None
):
self._state = float(activity_data["heatingPower"]["percentage"])
self._state_attributes = {
"time": activity_data["heatingPower"]["timestamp"]
}
elif self.zone_variable == "ac":
self._state = self._tado_zone_data.ac_power
self._state_attributes = {"time": self._tado_zone_data.ac_power_timestamp}
if "activityDataPoints" in data:
activity_data = data["activityDataPoints"]
if "acPower" in activity_data and activity_data["acPower"] is not None:
self._state = activity_data["acPower"]["value"]
self._state_attributes = {
"time": activity_data["acPower"]["timestamp"]
}
elif self.zone_variable == "tado bridge status":
self._state = self._tado_zone_data.connection
if "connectionState" in data:
self._state = data["connectionState"]["value"]
elif self.zone_variable == "tado mode":
self._state = self._tado_zone_data.tado_mode
if "tadoMode" in data:
self._state = data["tadoMode"]
elif self.zone_variable == "overlay":
self._state = self._tado_zone_data.overlay_active
self._state = "overlay" in data and data["overlay"] is not None
self._state_attributes = (
{"termination": self._tado_zone_data.overlay_termination_type}
if self._tado_zone_data.overlay_active
{"termination": data["overlay"]["termination"]["type"]}
if self._state
else {}
)
elif self.zone_variable == "early start":
self._state = self._tado_zone_data.preparation is not None
self._state = "preparation" in data and data["preparation"] is not None
elif self.zone_variable == "open window":
self._state = self._tado_zone_data.open_window is not None
self._state_attributes = self._tado_zone_data.open_window_attr
self._state = "openWindow" in data and data["openWindow"] is not None
self._state_attributes = data["openWindow"] if self._state else {}

View file

@ -1,285 +0,0 @@
"""Adapter to represent a tado zones and state."""
import logging
from homeassistant.components.climate.const import (
CURRENT_HVAC_COOL,
CURRENT_HVAC_HEAT,
CURRENT_HVAC_IDLE,
CURRENT_HVAC_OFF,
)
from .const import (
CONST_FAN_AUTO,
CONST_FAN_OFF,
CONST_LINK_OFFLINE,
CONST_MODE_OFF,
CONST_MODE_SMART_SCHEDULE,
DEFAULT_TADO_PRECISION,
TADO_MODES_TO_HA_CURRENT_HVAC_ACTION,
)
_LOGGER = logging.getLogger(__name__)
class TadoZoneData:
"""Represent a tado zone."""
def __init__(self, data, zone_id):
"""Create a tado zone."""
self._data = data
self._zone_id = zone_id
self._current_temp = None
self._connection = None
self._current_temp_timestamp = None
self._current_humidity = None
self._is_away = False
self._current_hvac_action = None
self._current_tado_fan_speed = None
self._current_tado_hvac_mode = None
self._target_temp = None
self._available = False
self._power = None
self._link = None
self._ac_power_timestamp = None
self._heating_power_timestamp = None
self._ac_power = None
self._heating_power = None
self._heating_power_percentage = None
self._tado_mode = None
self._overlay_active = None
self._overlay_termination_type = None
self._preparation = None
self._open_window = None
self._open_window_attr = None
self._precision = DEFAULT_TADO_PRECISION
self.update_data(data)
@property
def preparation(self):
"""Zone is preparing to heat."""
return self._preparation
@property
def open_window(self):
"""Window is open."""
return self._open_window
@property
def open_window_attr(self):
"""Window open attributes."""
return self._open_window_attr
@property
def current_temp(self):
"""Temperature of the zone."""
return self._current_temp
@property
def current_temp_timestamp(self):
"""Temperature of the zone timestamp."""
return self._current_temp_timestamp
@property
def connection(self):
"""Up or down internet connection."""
return self._connection
@property
def tado_mode(self):
"""Tado mode."""
return self._tado_mode
@property
def overlay_active(self):
"""Overlay acitive."""
return self._current_tado_hvac_mode != CONST_MODE_SMART_SCHEDULE
@property
def overlay_termination_type(self):
"""Overlay termination type (what happens when period ends)."""
return self._overlay_termination_type
@property
def current_humidity(self):
"""Humidity of the zone."""
return self._current_humidity
@property
def current_humidity_timestamp(self):
"""Humidity of the zone timestamp."""
return self._current_humidity_timestamp
@property
def ac_power_timestamp(self):
"""AC power timestamp."""
return self._ac_power_timestamp
@property
def heating_power_timestamp(self):
"""Heating power timestamp."""
return self._heating_power_timestamp
@property
def ac_power(self):
"""AC power."""
return self._ac_power
@property
def heating_power(self):
"""Heating power."""
return self._heating_power
@property
def heating_power_percentage(self):
"""Heating power percentage."""
return self._heating_power_percentage
@property
def is_away(self):
"""Is Away (not home)."""
return self._is_away
@property
def power(self):
"""Power is on."""
return self._power
@property
def current_hvac_action(self):
"""HVAC Action (home assistant const)."""
return self._current_hvac_action
@property
def current_tado_fan_speed(self):
"""TADO Fan speed (tado const)."""
return self._current_tado_fan_speed
@property
def link(self):
"""Link (internet connection state)."""
return self._link
@property
def precision(self):
"""Precision of temp units."""
return self._precision
@property
def current_tado_hvac_mode(self):
"""TADO HVAC Mode (tado const)."""
return self._current_tado_hvac_mode
@property
def target_temp(self):
"""Target temperature (C)."""
return self._target_temp
@property
def available(self):
"""Device is available and link is up."""
return self._available
def update_data(self, data):
"""Handle update callbacks."""
_LOGGER.debug("Updating climate platform for zone %d", self._zone_id)
if "sensorDataPoints" in data:
sensor_data = data["sensorDataPoints"]
if "insideTemperature" in sensor_data:
temperature = float(sensor_data["insideTemperature"]["celsius"])
self._current_temp = temperature
self._current_temp_timestamp = sensor_data["insideTemperature"][
"timestamp"
]
if "precision" in sensor_data["insideTemperature"]:
self._precision = sensor_data["insideTemperature"]["precision"][
"celsius"
]
if "humidity" in sensor_data:
humidity = float(sensor_data["humidity"]["percentage"])
self._current_humidity = humidity
self._current_humidity_timestamp = sensor_data["humidity"]["timestamp"]
self._is_away = None
self._tado_mode = None
if "tadoMode" in data:
self._is_away = data["tadoMode"] == "AWAY"
self._tado_mode = data["tadoMode"]
self._link = None
if "link" in data:
self._link = data["link"]["state"]
self._current_hvac_action = CURRENT_HVAC_OFF
if "setting" in data:
# temperature setting will not exist when device is off
if (
"temperature" in data["setting"]
and data["setting"]["temperature"] is not None
):
setting = float(data["setting"]["temperature"]["celsius"])
self._target_temp = setting
setting = data["setting"]
self._current_tado_fan_speed = CONST_FAN_OFF
# If there is no overlay, the mode will always be
# "SMART_SCHEDULE"
if "mode" in setting:
self._current_tado_hvac_mode = setting["mode"]
else:
self._current_tado_hvac_mode = CONST_MODE_OFF
self._power = setting["power"]
if self._power == "ON":
# Not all devices have fans
self._current_tado_fan_speed = setting.get("fanSpeed", CONST_FAN_AUTO)
self._current_hvac_action = CURRENT_HVAC_IDLE
self._preparation = "preparation" in data and data["preparation"] is not None
self._open_window = "openWindow" in data and data["openWindow"] is not None
self._open_window_attr = data["openWindow"] if self._open_window else {}
if "activityDataPoints" in data:
activity_data = data["activityDataPoints"]
if "acPower" in activity_data and activity_data["acPower"] is not None:
self._ac_power = activity_data["acPower"]["value"]
self._ac_power_timestamp = activity_data["acPower"]["timestamp"]
if activity_data["acPower"]["value"] == "ON" and self._power == "ON":
# acPower means the unit has power so we need to map the mode
self._current_hvac_action = TADO_MODES_TO_HA_CURRENT_HVAC_ACTION.get(
self._current_tado_hvac_mode, CURRENT_HVAC_COOL
)
if (
"heatingPower" in activity_data
and activity_data["heatingPower"] is not None
):
self._heating_power = activity_data["heatingPower"].get("value", None)
self._heating_power_timestamp = activity_data["heatingPower"][
"timestamp"
]
self._heating_power_percentage = float(
activity_data["heatingPower"].get("percentage", 0)
)
if self._heating_power_percentage > 0.0 and self._power == "ON":
self._current_hvac_action = CURRENT_HVAC_HEAT
# If there is no overlay
# then we are running the smart schedule
self._overlay_termination_type = None
if "overlay" in data and data["overlay"] is not None:
if (
"termination" in data["overlay"]
and "type" in data["overlay"]["termination"]
):
self._overlay_termination_type = data["overlay"]["termination"]["type"]
else:
self._current_tado_hvac_mode = CONST_MODE_SMART_SCHEDULE
self._connection = (
data["connectionState"]["value"] if "connectionState" in data else None
)
self._available = self._link != CONST_LINK_OFFLINE

View file

@ -12,7 +12,6 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect
from . import DOMAIN, SIGNAL_TADO_UPDATE_RECEIVED
from .const import (
CONST_MODE_HEAT,
CONST_MODE_OFF,
CONST_MODE_SMART_SCHEDULE,
CONST_OVERLAY_MANUAL,
@ -52,16 +51,14 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
for tado in api_list:
for zone in tado.zones:
if zone["type"] in [TYPE_HOT_WATER]:
entity = create_water_heater_entity(
hass, tado, zone["name"], zone["id"]
)
entity = create_water_heater_entity(tado, zone["name"], zone["id"])
entities.append(entity)
if entities:
add_entities(entities, True)
def create_water_heater_entity(hass, tado, name: str, zone_id: int):
def create_water_heater_entity(tado, name: str, zone_id: int):
"""Create a Tado water heater device."""
capabilities = tado.get_capabilities(zone_id)
supports_temperature_control = capabilities["canSetTemperature"]
@ -75,7 +72,7 @@ def create_water_heater_entity(hass, tado, name: str, zone_id: int):
max_temp = None
entity = TadoWaterHeater(
hass, tado, name, zone_id, supports_temperature_control, min_temp, max_temp
tado, name, zone_id, supports_temperature_control, min_temp, max_temp
)
return entity
@ -86,7 +83,6 @@ class TadoWaterHeater(WaterHeaterDevice):
def __init__(
self,
hass,
tado,
zone_name,
zone_id,
@ -95,7 +91,6 @@ class TadoWaterHeater(WaterHeaterDevice):
max_temp,
):
"""Initialize of Tado water heater entity."""
self.hass = hass
self._tado = tado
self.zone_name = zone_name
@ -115,17 +110,28 @@ class TadoWaterHeater(WaterHeaterDevice):
if self._supports_temperature_control:
self._supported_features |= SUPPORT_TARGET_TEMPERATURE
self._current_tado_heat_mode = CONST_MODE_SMART_SCHEDULE
if tado.fallback:
# Fallback to Smart Schedule at next Schedule switch
self._default_overlay = CONST_OVERLAY_TADO_MODE
else:
# Don't fallback to Smart Schedule, but keep in manual mode
self._default_overlay = CONST_OVERLAY_MANUAL
self._current_operation = CONST_MODE_SMART_SCHEDULE
self._overlay_mode = CONST_MODE_SMART_SCHEDULE
self._async_update_data()
async def async_added_to_hass(self):
"""Register for sensor updates."""
@callback
def async_update_callback():
"""Schedule an entity update."""
self.async_schedule_update_ha_state(True)
async_dispatcher_connect(
self.hass,
SIGNAL_TADO_UPDATE_RECEIVED.format("zone", self.zone_id),
self._async_update_callback,
async_update_callback,
)
@property
@ -151,7 +157,7 @@ class TadoWaterHeater(WaterHeaterDevice):
@property
def current_operation(self):
"""Return current readable operation mode."""
return WATER_HEATER_MAP_TADO.get(self._current_tado_heat_mode)
return WATER_HEATER_MAP_TADO.get(self._current_operation)
@property
def target_temperature(self):
@ -192,9 +198,16 @@ class TadoWaterHeater(WaterHeaterDevice):
elif operation_mode == MODE_AUTO:
mode = CONST_MODE_SMART_SCHEDULE
elif operation_mode == MODE_HEAT:
mode = CONST_MODE_HEAT
mode = self._default_overlay
self._control_heater(heat_mode=mode)
self._current_operation = mode
self._overlay_mode = None
# Set a target temperature if we don't have any
if mode == CONST_OVERLAY_TADO_MODE and self._target_temp is None:
self._target_temp = self.min_temp
self._control_heater()
def set_temperature(self, **kwargs):
"""Set new target temperature."""
@ -202,17 +215,13 @@ class TadoWaterHeater(WaterHeaterDevice):
if not self._supports_temperature_control or temperature is None:
return
self._control_heater(target_temp=temperature)
self._current_operation = self._default_overlay
self._overlay_mode = None
self._target_temp = temperature
self._control_heater()
@callback
def _async_update_callback(self):
"""Load tado data and update state."""
self._async_update_data()
self.async_write_ha_state()
@callback
def _async_update_data(self):
"""Load tado data."""
def update(self):
"""Handle update callbacks."""
_LOGGER.debug("Updating water_heater platform for zone %d", self.zone_id)
data = self._tado.data["zone"][self.zone_id]
@ -223,70 +232,71 @@ class TadoWaterHeater(WaterHeaterDevice):
if "setting" in data:
power = data["setting"]["power"]
if power == "OFF":
self._current_tado_heat_mode = CONST_MODE_OFF
self._current_operation = CONST_MODE_OFF
# There is no overlay, the mode will always be
# "SMART_SCHEDULE"
self._overlay_mode = CONST_MODE_SMART_SCHEDULE
self._device_is_active = False
else:
self._current_tado_heat_mode = CONST_MODE_HEAT
self._device_is_active = True
# temperature setting will not exist when device is off
if (
"temperature" in data["setting"]
and data["setting"]["temperature"] is not None
):
self._target_temp = float(data["setting"]["temperature"]["celsius"])
setting = float(data["setting"]["temperature"]["celsius"])
self._target_temp = setting
# If there is no overlay
# then we are running the smart schedule
if "overlay" in data and data["overlay"] is None:
self._current_tado_heat_mode = CONST_MODE_SMART_SCHEDULE
overlay = False
overlay_data = None
termination = CONST_MODE_SMART_SCHEDULE
self.async_write_ha_state()
if "overlay" in data:
overlay_data = data["overlay"]
overlay = overlay_data is not None
def _control_heater(self, heat_mode=None, target_temp=None):
if overlay:
termination = overlay_data["termination"]["type"]
if self._device_is_active:
# If you set mode manually to off, there will be an overlay
# and a termination, but we want to see the mode "OFF"
self._overlay_mode = termination
self._current_operation = termination
def _control_heater(self):
"""Send new target temperature."""
if heat_mode:
self._current_tado_heat_mode = heat_mode
if target_temp:
self._target_temp = target_temp
# Set a target temperature if we don't have any
if self._target_temp is None:
self._target_temp = self.min_temp
if self._current_tado_heat_mode == CONST_MODE_SMART_SCHEDULE:
if self._current_operation == CONST_MODE_SMART_SCHEDULE:
_LOGGER.debug(
"Switching to SMART_SCHEDULE for zone %s (%d)",
self.zone_name,
self.zone_id,
)
self._tado.reset_zone_overlay(self.zone_id)
self._overlay_mode = self._current_operation
return
if self._current_tado_heat_mode == CONST_MODE_OFF:
if self._current_operation == CONST_MODE_OFF:
_LOGGER.debug(
"Switching to OFF for zone %s (%d)", self.zone_name, self.zone_id
)
self._tado.set_zone_off(self.zone_id, CONST_OVERLAY_MANUAL, TYPE_HOT_WATER)
self._overlay_mode = self._current_operation
return
# Fallback to Smart Schedule at next Schedule switch if we have fallback enabled
overlay_mode = (
CONST_OVERLAY_TADO_MODE if self._tado.fallback else CONST_OVERLAY_MANUAL
)
_LOGGER.debug(
"Switching to %s for zone %s (%d) with temperature %s",
self._current_tado_heat_mode,
self._current_operation,
self.zone_name,
self.zone_id,
self._target_temp,
)
self._tado.set_zone_overlay(
zone_id=self.zone_id,
overlay_mode=overlay_mode,
temperature=self._target_temp,
duration=None,
device_type=TYPE_HOT_WATER,
self.zone_id,
self._current_operation,
self._target_temp,
None,
TYPE_HOT_WATER,
)
self._overlay_mode = self._current_tado_heat_mode
self._overlay_mode = self._current_operation

View file

@ -1650,7 +1650,7 @@ python-songpal==0.11.2
python-synology==0.4.0
# homeassistant.components.tado
python-tado==0.4.0
python-tado==0.3.0
# homeassistant.components.telegram_bot
python-telegram-bot==11.1.0

View file

@ -580,9 +580,6 @@ python-miio==0.4.8
# homeassistant.components.nest
python-nest==4.1.0
# homeassistant.components.tado
python-tado==0.4.0
# homeassistant.components.twitch
python-twitch-client==0.6.0

View file

@ -1,18 +0,0 @@
"""Mocks for the tado component."""
import json
import os
from homeassistant.components.tado.tado_adapter import TadoZoneData
from tests.common import load_fixture
async def _mock_tado_climate_zone_from_fixture(hass, file):
return TadoZoneData(await _load_json_fixture(hass, file), 1)
async def _load_json_fixture(hass, path):
fixture = await hass.async_add_executor_job(
load_fixture, os.path.join("tado", path)
)
return json.loads(fixture)

View file

@ -1,423 +0,0 @@
"""The tado_adapter tests for the tado platform."""
from tests.components.tado.mocks import _mock_tado_climate_zone_from_fixture
async def test_ac_issue_32294_heat_mode(hass):
"""Test smart ac cool mode."""
ac_issue_32294_heat_mode = await _mock_tado_climate_zone_from_fixture(
hass, "ac_issue_32294.heat_mode.json"
)
assert ac_issue_32294_heat_mode.preparation is False
assert ac_issue_32294_heat_mode.open_window is False
assert ac_issue_32294_heat_mode.open_window_attr == {}
assert ac_issue_32294_heat_mode.current_temp == 21.82
assert ac_issue_32294_heat_mode.current_temp_timestamp == "2020-02-29T22:51:05.016Z"
assert ac_issue_32294_heat_mode.connection is None
assert ac_issue_32294_heat_mode.tado_mode == "HOME"
assert ac_issue_32294_heat_mode.overlay_active is False
assert ac_issue_32294_heat_mode.overlay_termination_type is None
assert ac_issue_32294_heat_mode.current_humidity == 40.4
assert (
ac_issue_32294_heat_mode.current_humidity_timestamp
== "2020-02-29T22:51:05.016Z"
)
assert ac_issue_32294_heat_mode.ac_power_timestamp == "2020-02-29T22:50:34.850Z"
assert ac_issue_32294_heat_mode.heating_power_timestamp is None
assert ac_issue_32294_heat_mode.ac_power == "ON"
assert ac_issue_32294_heat_mode.heating_power is None
assert ac_issue_32294_heat_mode.heating_power_percentage is None
assert ac_issue_32294_heat_mode.is_away is False
assert ac_issue_32294_heat_mode.power == "ON"
assert ac_issue_32294_heat_mode.current_hvac_action == "heating"
assert ac_issue_32294_heat_mode.current_tado_fan_speed == "AUTO"
assert ac_issue_32294_heat_mode.link == "ONLINE"
assert ac_issue_32294_heat_mode.current_tado_hvac_mode == "SMART_SCHEDULE"
assert ac_issue_32294_heat_mode.target_temp == 25.0
assert ac_issue_32294_heat_mode.available is True
assert ac_issue_32294_heat_mode.precision == 0.1
async def test_smartac3_smart_mode(hass):
"""Test smart ac smart mode."""
smartac3_smart_mode = await _mock_tado_climate_zone_from_fixture(
hass, "smartac3.smart_mode.json"
)
assert smartac3_smart_mode.preparation is False
assert smartac3_smart_mode.open_window is False
assert smartac3_smart_mode.open_window_attr == {}
assert smartac3_smart_mode.current_temp == 24.43
assert smartac3_smart_mode.current_temp_timestamp == "2020-03-05T03:50:24.769Z"
assert smartac3_smart_mode.connection is None
assert smartac3_smart_mode.tado_mode == "HOME"
assert smartac3_smart_mode.overlay_active is False
assert smartac3_smart_mode.overlay_termination_type is None
assert smartac3_smart_mode.current_humidity == 60.0
assert smartac3_smart_mode.current_humidity_timestamp == "2020-03-05T03:50:24.769Z"
assert smartac3_smart_mode.ac_power_timestamp == "2020-03-05T03:52:22.253Z"
assert smartac3_smart_mode.heating_power_timestamp is None
assert smartac3_smart_mode.ac_power == "OFF"
assert smartac3_smart_mode.heating_power is None
assert smartac3_smart_mode.heating_power_percentage is None
assert smartac3_smart_mode.is_away is False
assert smartac3_smart_mode.power == "ON"
assert smartac3_smart_mode.current_hvac_action == "idle"
assert smartac3_smart_mode.current_tado_fan_speed == "MIDDLE"
assert smartac3_smart_mode.link == "ONLINE"
assert smartac3_smart_mode.current_tado_hvac_mode == "SMART_SCHEDULE"
assert smartac3_smart_mode.target_temp == 20.0
assert smartac3_smart_mode.available is True
assert smartac3_smart_mode.precision == 0.1
async def test_smartac3_cool_mode(hass):
"""Test smart ac cool mode."""
smartac3_cool_mode = await _mock_tado_climate_zone_from_fixture(
hass, "smartac3.cool_mode.json"
)
assert smartac3_cool_mode.preparation is False
assert smartac3_cool_mode.open_window is False
assert smartac3_cool_mode.open_window_attr == {}
assert smartac3_cool_mode.current_temp == 24.76
assert smartac3_cool_mode.current_temp_timestamp == "2020-03-05T03:57:38.850Z"
assert smartac3_cool_mode.connection is None
assert smartac3_cool_mode.tado_mode == "HOME"
assert smartac3_cool_mode.overlay_active is True
assert smartac3_cool_mode.overlay_termination_type == "TADO_MODE"
assert smartac3_cool_mode.current_humidity == 60.9
assert smartac3_cool_mode.current_humidity_timestamp == "2020-03-05T03:57:38.850Z"
assert smartac3_cool_mode.ac_power_timestamp == "2020-03-05T04:01:07.162Z"
assert smartac3_cool_mode.heating_power_timestamp is None
assert smartac3_cool_mode.ac_power == "ON"
assert smartac3_cool_mode.heating_power is None
assert smartac3_cool_mode.heating_power_percentage is None
assert smartac3_cool_mode.is_away is False
assert smartac3_cool_mode.power == "ON"
assert smartac3_cool_mode.current_hvac_action == "cooling"
assert smartac3_cool_mode.current_tado_fan_speed == "AUTO"
assert smartac3_cool_mode.link == "ONLINE"
assert smartac3_cool_mode.current_tado_hvac_mode == "COOL"
assert smartac3_cool_mode.target_temp == 17.78
assert smartac3_cool_mode.available is True
assert smartac3_cool_mode.precision == 0.1
async def test_smartac3_auto_mode(hass):
"""Test smart ac cool mode."""
smartac3_auto_mode = await _mock_tado_climate_zone_from_fixture(
hass, "smartac3.auto_mode.json"
)
assert smartac3_auto_mode.preparation is False
assert smartac3_auto_mode.open_window is False
assert smartac3_auto_mode.open_window_attr == {}
assert smartac3_auto_mode.current_temp == 24.8
assert smartac3_auto_mode.current_temp_timestamp == "2020-03-05T03:55:38.160Z"
assert smartac3_auto_mode.connection is None
assert smartac3_auto_mode.tado_mode == "HOME"
assert smartac3_auto_mode.overlay_active is True
assert smartac3_auto_mode.overlay_termination_type == "TADO_MODE"
assert smartac3_auto_mode.current_humidity == 62.5
assert smartac3_auto_mode.current_humidity_timestamp == "2020-03-05T03:55:38.160Z"
assert smartac3_auto_mode.ac_power_timestamp == "2020-03-05T03:56:38.627Z"
assert smartac3_auto_mode.heating_power_timestamp is None
assert smartac3_auto_mode.ac_power == "ON"
assert smartac3_auto_mode.heating_power is None
assert smartac3_auto_mode.heating_power_percentage is None
assert smartac3_auto_mode.is_away is False
assert smartac3_auto_mode.power == "ON"
assert smartac3_auto_mode.current_hvac_action == "cooling"
assert smartac3_auto_mode.current_tado_fan_speed == "AUTO"
assert smartac3_auto_mode.link == "ONLINE"
assert smartac3_auto_mode.current_tado_hvac_mode == "AUTO"
assert smartac3_auto_mode.target_temp is None
assert smartac3_auto_mode.available is True
assert smartac3_auto_mode.precision == 0.1
async def test_smartac3_dry_mode(hass):
"""Test smart ac cool mode."""
smartac3_dry_mode = await _mock_tado_climate_zone_from_fixture(
hass, "smartac3.dry_mode.json"
)
assert smartac3_dry_mode.preparation is False
assert smartac3_dry_mode.open_window is False
assert smartac3_dry_mode.open_window_attr == {}
assert smartac3_dry_mode.current_temp == 25.01
assert smartac3_dry_mode.current_temp_timestamp == "2020-03-05T04:02:07.396Z"
assert smartac3_dry_mode.connection is None
assert smartac3_dry_mode.tado_mode == "HOME"
assert smartac3_dry_mode.overlay_active is True
assert smartac3_dry_mode.overlay_termination_type == "TADO_MODE"
assert smartac3_dry_mode.current_humidity == 62.0
assert smartac3_dry_mode.current_humidity_timestamp == "2020-03-05T04:02:07.396Z"
assert smartac3_dry_mode.ac_power_timestamp == "2020-03-05T04:02:40.867Z"
assert smartac3_dry_mode.heating_power_timestamp is None
assert smartac3_dry_mode.ac_power == "ON"
assert smartac3_dry_mode.heating_power is None
assert smartac3_dry_mode.heating_power_percentage is None
assert smartac3_dry_mode.is_away is False
assert smartac3_dry_mode.power == "ON"
assert smartac3_dry_mode.current_hvac_action == "drying"
assert smartac3_dry_mode.current_tado_fan_speed == "AUTO"
assert smartac3_dry_mode.link == "ONLINE"
assert smartac3_dry_mode.current_tado_hvac_mode == "DRY"
assert smartac3_dry_mode.target_temp is None
assert smartac3_dry_mode.available is True
assert smartac3_dry_mode.precision == 0.1
async def test_smartac3_fan_mode(hass):
"""Test smart ac cool mode."""
smartac3_fan_mode = await _mock_tado_climate_zone_from_fixture(
hass, "smartac3.fan_mode.json"
)
assert smartac3_fan_mode.preparation is False
assert smartac3_fan_mode.open_window is False
assert smartac3_fan_mode.open_window_attr == {}
assert smartac3_fan_mode.current_temp == 25.01
assert smartac3_fan_mode.current_temp_timestamp == "2020-03-05T04:02:07.396Z"
assert smartac3_fan_mode.connection is None
assert smartac3_fan_mode.tado_mode == "HOME"
assert smartac3_fan_mode.overlay_active is True
assert smartac3_fan_mode.overlay_termination_type == "TADO_MODE"
assert smartac3_fan_mode.current_humidity == 62.0
assert smartac3_fan_mode.current_humidity_timestamp == "2020-03-05T04:02:07.396Z"
assert smartac3_fan_mode.ac_power_timestamp == "2020-03-05T04:03:44.328Z"
assert smartac3_fan_mode.heating_power_timestamp is None
assert smartac3_fan_mode.ac_power == "ON"
assert smartac3_fan_mode.heating_power is None
assert smartac3_fan_mode.heating_power_percentage is None
assert smartac3_fan_mode.is_away is False
assert smartac3_fan_mode.power == "ON"
assert smartac3_fan_mode.current_hvac_action == "fan"
assert smartac3_fan_mode.current_tado_fan_speed == "AUTO"
assert smartac3_fan_mode.link == "ONLINE"
assert smartac3_fan_mode.current_tado_hvac_mode == "FAN"
assert smartac3_fan_mode.target_temp is None
assert smartac3_fan_mode.available is True
assert smartac3_fan_mode.precision == 0.1
async def test_smartac3_heat_mode(hass):
"""Test smart ac cool mode."""
smartac3_heat_mode = await _mock_tado_climate_zone_from_fixture(
hass, "smartac3.heat_mode.json"
)
assert smartac3_heat_mode.preparation is False
assert smartac3_heat_mode.open_window is False
assert smartac3_heat_mode.open_window_attr == {}
assert smartac3_heat_mode.current_temp == 24.76
assert smartac3_heat_mode.current_temp_timestamp == "2020-03-05T03:57:38.850Z"
assert smartac3_heat_mode.connection is None
assert smartac3_heat_mode.tado_mode == "HOME"
assert smartac3_heat_mode.overlay_active is True
assert smartac3_heat_mode.overlay_termination_type == "TADO_MODE"
assert smartac3_heat_mode.current_humidity == 60.9
assert smartac3_heat_mode.current_humidity_timestamp == "2020-03-05T03:57:38.850Z"
assert smartac3_heat_mode.ac_power_timestamp == "2020-03-05T03:59:36.390Z"
assert smartac3_heat_mode.heating_power_timestamp is None
assert smartac3_heat_mode.ac_power == "ON"
assert smartac3_heat_mode.heating_power is None
assert smartac3_heat_mode.heating_power_percentage is None
assert smartac3_heat_mode.is_away is False
assert smartac3_heat_mode.power == "ON"
assert smartac3_heat_mode.current_hvac_action == "heating"
assert smartac3_heat_mode.current_tado_fan_speed == "AUTO"
assert smartac3_heat_mode.link == "ONLINE"
assert smartac3_heat_mode.current_tado_hvac_mode == "HEAT"
assert smartac3_heat_mode.target_temp == 16.11
assert smartac3_heat_mode.available is True
assert smartac3_heat_mode.precision == 0.1
async def test_smartac3_hvac_off(hass):
"""Test smart ac cool mode."""
smartac3_hvac_off = await _mock_tado_climate_zone_from_fixture(
hass, "smartac3.hvac_off.json"
)
assert smartac3_hvac_off.preparation is False
assert smartac3_hvac_off.open_window is False
assert smartac3_hvac_off.open_window_attr == {}
assert smartac3_hvac_off.current_temp == 21.44
assert smartac3_hvac_off.current_temp_timestamp == "2020-03-05T01:21:44.089Z"
assert smartac3_hvac_off.connection is None
assert smartac3_hvac_off.tado_mode == "AWAY"
assert smartac3_hvac_off.overlay_active is True
assert smartac3_hvac_off.overlay_termination_type == "MANUAL"
assert smartac3_hvac_off.current_humidity == 48.2
assert smartac3_hvac_off.current_humidity_timestamp == "2020-03-05T01:21:44.089Z"
assert smartac3_hvac_off.ac_power_timestamp == "2020-02-29T05:34:10.318Z"
assert smartac3_hvac_off.heating_power_timestamp is None
assert smartac3_hvac_off.ac_power == "OFF"
assert smartac3_hvac_off.heating_power is None
assert smartac3_hvac_off.heating_power_percentage is None
assert smartac3_hvac_off.is_away is True
assert smartac3_hvac_off.power == "OFF"
assert smartac3_hvac_off.current_hvac_action == "off"
assert smartac3_hvac_off.current_tado_fan_speed == "OFF"
assert smartac3_hvac_off.link == "ONLINE"
assert smartac3_hvac_off.current_tado_hvac_mode == "OFF"
assert smartac3_hvac_off.target_temp is None
assert smartac3_hvac_off.available is True
assert smartac3_hvac_off.precision == 0.1
async def test_smartac3_manual_off(hass):
"""Test smart ac cool mode."""
smartac3_manual_off = await _mock_tado_climate_zone_from_fixture(
hass, "smartac3.manual_off.json"
)
assert smartac3_manual_off.preparation is False
assert smartac3_manual_off.open_window is False
assert smartac3_manual_off.open_window_attr == {}
assert smartac3_manual_off.current_temp == 25.01
assert smartac3_manual_off.current_temp_timestamp == "2020-03-05T04:02:07.396Z"
assert smartac3_manual_off.connection is None
assert smartac3_manual_off.tado_mode == "HOME"
assert smartac3_manual_off.overlay_active is True
assert smartac3_manual_off.overlay_termination_type == "MANUAL"
assert smartac3_manual_off.current_humidity == 62.0
assert smartac3_manual_off.current_humidity_timestamp == "2020-03-05T04:02:07.396Z"
assert smartac3_manual_off.ac_power_timestamp == "2020-03-05T04:05:08.804Z"
assert smartac3_manual_off.heating_power_timestamp is None
assert smartac3_manual_off.ac_power == "OFF"
assert smartac3_manual_off.heating_power is None
assert smartac3_manual_off.heating_power_percentage is None
assert smartac3_manual_off.is_away is False
assert smartac3_manual_off.power == "OFF"
assert smartac3_manual_off.current_hvac_action == "off"
assert smartac3_manual_off.current_tado_fan_speed == "OFF"
assert smartac3_manual_off.link == "ONLINE"
assert smartac3_manual_off.current_tado_hvac_mode == "OFF"
assert smartac3_manual_off.target_temp is None
assert smartac3_manual_off.available is True
assert smartac3_manual_off.precision == 0.1
async def test_smartac3_offline(hass):
"""Test smart ac cool mode."""
smartac3_offline = await _mock_tado_climate_zone_from_fixture(
hass, "smartac3.offline.json"
)
assert smartac3_offline.preparation is False
assert smartac3_offline.open_window is False
assert smartac3_offline.open_window_attr == {}
assert smartac3_offline.current_temp == 25.05
assert smartac3_offline.current_temp_timestamp == "2020-03-03T21:23:57.846Z"
assert smartac3_offline.connection is None
assert smartac3_offline.tado_mode == "HOME"
assert smartac3_offline.overlay_active is True
assert smartac3_offline.overlay_termination_type == "TADO_MODE"
assert smartac3_offline.current_humidity == 61.6
assert smartac3_offline.current_humidity_timestamp == "2020-03-03T21:23:57.846Z"
assert smartac3_offline.ac_power_timestamp == "2020-02-29T18:42:26.683Z"
assert smartac3_offline.heating_power_timestamp is None
assert smartac3_offline.ac_power == "OFF"
assert smartac3_offline.heating_power is None
assert smartac3_offline.heating_power_percentage is None
assert smartac3_offline.is_away is False
assert smartac3_offline.power == "ON"
assert smartac3_offline.current_hvac_action == "idle"
assert smartac3_offline.current_tado_fan_speed == "AUTO"
assert smartac3_offline.link == "OFFLINE"
assert smartac3_offline.current_tado_hvac_mode == "COOL"
assert smartac3_offline.target_temp == 17.78
assert smartac3_offline.available is False
assert smartac3_offline.precision == 0.1
async def test_hvac_action_heat(hass):
"""Test smart ac cool mode."""
hvac_action_heat = await _mock_tado_climate_zone_from_fixture(
hass, "hvac_action_heat.json"
)
assert hvac_action_heat.preparation is False
assert hvac_action_heat.open_window is False
assert hvac_action_heat.open_window_attr == {}
assert hvac_action_heat.current_temp == 21.4
assert hvac_action_heat.current_temp_timestamp == "2020-03-06T18:06:09.546Z"
assert hvac_action_heat.connection is None
assert hvac_action_heat.tado_mode == "HOME"
assert hvac_action_heat.overlay_active is True
assert hvac_action_heat.overlay_termination_type == "TADO_MODE"
assert hvac_action_heat.current_humidity == 50.4
assert hvac_action_heat.current_humidity_timestamp == "2020-03-06T18:06:09.546Z"
assert hvac_action_heat.ac_power_timestamp == "2020-03-06T17:38:30.302Z"
assert hvac_action_heat.heating_power_timestamp is None
assert hvac_action_heat.ac_power == "OFF"
assert hvac_action_heat.heating_power is None
assert hvac_action_heat.heating_power_percentage is None
assert hvac_action_heat.is_away is False
assert hvac_action_heat.power == "ON"
assert hvac_action_heat.current_hvac_action == "idle"
assert hvac_action_heat.current_tado_fan_speed == "AUTO"
assert hvac_action_heat.link == "ONLINE"
assert hvac_action_heat.current_tado_hvac_mode == "HEAT"
assert hvac_action_heat.target_temp == 16.11
assert hvac_action_heat.available is True
assert hvac_action_heat.precision == 0.1
async def test_smartac3_turning_off(hass):
"""Test smart ac cool mode."""
smartac3_turning_off = await _mock_tado_climate_zone_from_fixture(
hass, "smartac3.turning_off.json"
)
assert smartac3_turning_off.preparation is False
assert smartac3_turning_off.open_window is False
assert smartac3_turning_off.open_window_attr == {}
assert smartac3_turning_off.current_temp == 21.4
assert smartac3_turning_off.current_temp_timestamp == "2020-03-06T19:06:13.185Z"
assert smartac3_turning_off.connection is None
assert smartac3_turning_off.tado_mode == "HOME"
assert smartac3_turning_off.overlay_active is True
assert smartac3_turning_off.overlay_termination_type == "MANUAL"
assert smartac3_turning_off.current_humidity == 49.2
assert smartac3_turning_off.current_humidity_timestamp == "2020-03-06T19:06:13.185Z"
assert smartac3_turning_off.ac_power_timestamp == "2020-03-06T19:05:21.835Z"
assert smartac3_turning_off.heating_power_timestamp is None
assert smartac3_turning_off.ac_power == "ON"
assert smartac3_turning_off.heating_power is None
assert smartac3_turning_off.heating_power_percentage is None
assert smartac3_turning_off.is_away is False
assert smartac3_turning_off.power == "OFF"
assert smartac3_turning_off.current_hvac_action == "off"
assert smartac3_turning_off.current_tado_fan_speed == "OFF"
assert smartac3_turning_off.link == "ONLINE"
assert smartac3_turning_off.current_tado_hvac_mode == "OFF"
assert smartac3_turning_off.target_temp is None
assert smartac3_turning_off.available is True
assert smartac3_turning_off.precision == 0.1
async def test_michael_heat_mode(hass):
"""Test michael's tado."""
michael_heat_mode = await _mock_tado_climate_zone_from_fixture(
hass, "michael_heat_mode.json"
)
assert michael_heat_mode.preparation is False
assert michael_heat_mode.open_window is False
assert michael_heat_mode.open_window_attr == {}
assert michael_heat_mode.current_temp == 20.06
assert michael_heat_mode.current_temp_timestamp == "2020-03-09T08:16:49.271Z"
assert michael_heat_mode.connection is None
assert michael_heat_mode.tado_mode == "HOME"
assert michael_heat_mode.overlay_active is False
assert michael_heat_mode.overlay_termination_type is None
assert michael_heat_mode.current_humidity == 41.8
assert michael_heat_mode.current_humidity_timestamp == "2020-03-09T08:16:49.271Z"
assert michael_heat_mode.ac_power_timestamp is None
assert michael_heat_mode.heating_power_timestamp == "2020-03-09T08:20:47.299Z"
assert michael_heat_mode.ac_power is None
assert michael_heat_mode.heating_power is None
assert michael_heat_mode.heating_power_percentage == 0.0
assert michael_heat_mode.is_away is False
assert michael_heat_mode.power == "ON"
assert michael_heat_mode.current_hvac_action == "idle"
assert michael_heat_mode.current_tado_fan_speed == "AUTO"
assert michael_heat_mode.link == "ONLINE"
assert michael_heat_mode.current_tado_hvac_mode == "SMART_SCHEDULE"
assert michael_heat_mode.target_temp == 20.0
assert michael_heat_mode.available is True
assert michael_heat_mode.precision == 0.1

View file

@ -1,60 +0,0 @@
{
"tadoMode": "HOME",
"sensorDataPoints": {
"insideTemperature": {
"fahrenheit": 71.28,
"timestamp": "2020-02-29T22:51:05.016Z",
"celsius": 21.82,
"type": "TEMPERATURE",
"precision": {
"fahrenheit": 0.1,
"celsius": 0.1
}
},
"humidity": {
"timestamp": "2020-02-29T22:51:05.016Z",
"percentage": 40.4,
"type": "PERCENTAGE"
}
},
"link": {
"state": "ONLINE"
},
"openWindow": null,
"geolocationOverride": false,
"geolocationOverrideDisableTime": null,
"overlay": null,
"activityDataPoints": {
"acPower": {
"timestamp": "2020-02-29T22:50:34.850Z",
"type": "POWER",
"value": "ON"
}
},
"nextTimeBlock": {
"start": "2020-03-01T00:00:00.000Z"
},
"preparation": null,
"overlayType": null,
"nextScheduleChange": {
"start": "2020-03-01T00:00:00Z",
"setting": {
"type": "AIR_CONDITIONING",
"mode": "HEAT",
"power": "ON",
"temperature": {
"fahrenheit": 59.0,
"celsius": 15.0
}
}
},
"setting": {
"type": "AIR_CONDITIONING",
"mode": "HEAT",
"power": "ON",
"temperature": {
"fahrenheit": 77.0,
"celsius": 25.0
}
}
}

View file

@ -1,67 +0,0 @@
{
"tadoMode": "HOME",
"geolocationOverride": false,
"geolocationOverrideDisableTime": null,
"preparation": null,
"setting": {
"type": "AIR_CONDITIONING",
"power": "ON",
"mode": "HEAT",
"temperature": {
"celsius": 16.11,
"fahrenheit": 61.00
},
"fanSpeed": "AUTO"
},
"overlayType": "MANUAL",
"overlay": {
"type": "MANUAL",
"setting": {
"type": "AIR_CONDITIONING",
"power": "ON",
"mode": "HEAT",
"temperature": {
"celsius": 16.11,
"fahrenheit": 61.00
},
"fanSpeed": "AUTO"
},
"termination": {
"type": "TADO_MODE",
"typeSkillBasedApp": "TADO_MODE",
"projectedExpiry": null
}
},
"openWindow": null,
"nextScheduleChange": null,
"nextTimeBlock": {
"start": "2020-03-07T04:00:00.000Z"
},
"link": {
"state": "ONLINE"
},
"activityDataPoints": {
"acPower": {
"timestamp": "2020-03-06T17:38:30.302Z",
"type": "POWER",
"value": "OFF"
}
},
"sensorDataPoints": {
"insideTemperature": {
"celsius": 21.40,
"fahrenheit": 70.52,
"timestamp": "2020-03-06T18:06:09.546Z",
"type": "TEMPERATURE",
"precision": {
"celsius": 0.1,
"fahrenheit": 0.1
}
},
"humidity": {
"type": "PERCENTAGE",
"percentage": 50.40,
"timestamp": "2020-03-06T18:06:09.546Z"
}
}
}

View file

@ -1,58 +0,0 @@
{
"tadoMode": "HOME",
"geolocationOverride": false,
"geolocationOverrideDisableTime": null,
"preparation": null,
"setting": {
"type": "HEATING",
"power": "ON",
"temperature": {
"celsius": 20.0,
"fahrenheit": 68.0
}
},
"overlayType": null,
"overlay": null,
"openWindow": null,
"nextScheduleChange": {
"start": "2020-03-09T17:00:00Z",
"setting": {
"type": "HEATING",
"power": "ON",
"temperature": {
"celsius": 21.0,
"fahrenheit": 69.8
}
}
},
"nextTimeBlock": {
"start": "2020-03-09T17:00:00.000Z"
},
"link": {
"state": "ONLINE"
},
"activityDataPoints": {
"heatingPower": {
"type": "PERCENTAGE",
"percentage": 0.0,
"timestamp": "2020-03-09T08:20:47.299Z"
}
},
"sensorDataPoints": {
"insideTemperature": {
"celsius": 20.06,
"fahrenheit": 68.11,
"timestamp": "2020-03-09T08:16:49.271Z",
"type": "TEMPERATURE",
"precision": {
"celsius": 0.1,
"fahrenheit": 0.1
}
},
"humidity": {
"type": "PERCENTAGE",
"percentage": 41.8,
"timestamp": "2020-03-09T08:16:49.271Z"
}
}
}

View file

@ -1,57 +0,0 @@
{
"tadoMode": "HOME",
"sensorDataPoints": {
"insideTemperature": {
"fahrenheit": 76.64,
"timestamp": "2020-03-05T03:55:38.160Z",
"celsius": 24.8,
"type": "TEMPERATURE",
"precision": {
"fahrenheit": 0.1,
"celsius": 0.1
}
},
"humidity": {
"timestamp": "2020-03-05T03:55:38.160Z",
"percentage": 62.5,
"type": "PERCENTAGE"
}
},
"link": {
"state": "ONLINE"
},
"openWindow": null,
"geolocationOverride": false,
"geolocationOverrideDisableTime": null,
"overlay": {
"termination": {
"typeSkillBasedApp": "TADO_MODE",
"projectedExpiry": null,
"type": "TADO_MODE"
},
"setting": {
"type": "AIR_CONDITIONING",
"mode": "AUTO",
"power": "ON"
},
"type": "MANUAL"
},
"activityDataPoints": {
"acPower": {
"timestamp": "2020-03-05T03:56:38.627Z",
"type": "POWER",
"value": "ON"
}
},
"nextTimeBlock": {
"start": "2020-03-05T08:00:00.000Z"
},
"preparation": null,
"overlayType": "MANUAL",
"nextScheduleChange": null,
"setting": {
"type": "AIR_CONDITIONING",
"mode": "AUTO",
"power": "ON"
}
}

View file

@ -1,67 +0,0 @@
{
"tadoMode": "HOME",
"sensorDataPoints": {
"insideTemperature": {
"fahrenheit": 76.57,
"timestamp": "2020-03-05T03:57:38.850Z",
"celsius": 24.76,
"type": "TEMPERATURE",
"precision": {
"fahrenheit": 0.1,
"celsius": 0.1
}
},
"humidity": {
"timestamp": "2020-03-05T03:57:38.850Z",
"percentage": 60.9,
"type": "PERCENTAGE"
}
},
"link": {
"state": "ONLINE"
},
"openWindow": null,
"geolocationOverride": false,
"geolocationOverrideDisableTime": null,
"overlay": {
"termination": {
"typeSkillBasedApp": "TADO_MODE",
"projectedExpiry": null,
"type": "TADO_MODE"
},
"setting": {
"fanSpeed": "AUTO",
"type": "AIR_CONDITIONING",
"mode": "COOL",
"power": "ON",
"temperature": {
"fahrenheit": 64.0,
"celsius": 17.78
}
},
"type": "MANUAL"
},
"activityDataPoints": {
"acPower": {
"timestamp": "2020-03-05T04:01:07.162Z",
"type": "POWER",
"value": "ON"
}
},
"nextTimeBlock": {
"start": "2020-03-05T08:00:00.000Z"
},
"preparation": null,
"overlayType": "MANUAL",
"nextScheduleChange": null,
"setting": {
"fanSpeed": "AUTO",
"type": "AIR_CONDITIONING",
"mode": "COOL",
"power": "ON",
"temperature": {
"fahrenheit": 64.0,
"celsius": 17.78
}
}
}

View file

@ -1,57 +0,0 @@
{
"tadoMode": "HOME",
"sensorDataPoints": {
"insideTemperature": {
"fahrenheit": 77.02,
"timestamp": "2020-03-05T04:02:07.396Z",
"celsius": 25.01,
"type": "TEMPERATURE",
"precision": {
"fahrenheit": 0.1,
"celsius": 0.1
}
},
"humidity": {
"timestamp": "2020-03-05T04:02:07.396Z",
"percentage": 62.0,
"type": "PERCENTAGE"
}
},
"link": {
"state": "ONLINE"
},
"openWindow": null,
"geolocationOverride": false,
"geolocationOverrideDisableTime": null,
"overlay": {
"termination": {
"typeSkillBasedApp": "TADO_MODE",
"projectedExpiry": null,
"type": "TADO_MODE"
},
"setting": {
"type": "AIR_CONDITIONING",
"mode": "DRY",
"power": "ON"
},
"type": "MANUAL"
},
"activityDataPoints": {
"acPower": {
"timestamp": "2020-03-05T04:02:40.867Z",
"type": "POWER",
"value": "ON"
}
},
"nextTimeBlock": {
"start": "2020-03-05T08:00:00.000Z"
},
"preparation": null,
"overlayType": "MANUAL",
"nextScheduleChange": null,
"setting": {
"type": "AIR_CONDITIONING",
"mode": "DRY",
"power": "ON"
}
}

View file

@ -1,57 +0,0 @@
{
"tadoMode": "HOME",
"sensorDataPoints": {
"insideTemperature": {
"fahrenheit": 77.02,
"timestamp": "2020-03-05T04:02:07.396Z",
"celsius": 25.01,
"type": "TEMPERATURE",
"precision": {
"fahrenheit": 0.1,
"celsius": 0.1
}
},
"humidity": {
"timestamp": "2020-03-05T04:02:07.396Z",
"percentage": 62.0,
"type": "PERCENTAGE"
}
},
"link": {
"state": "ONLINE"
},
"openWindow": null,
"geolocationOverride": false,
"geolocationOverrideDisableTime": null,
"overlay": {
"termination": {
"typeSkillBasedApp": "TADO_MODE",
"projectedExpiry": null,
"type": "TADO_MODE"
},
"setting": {
"type": "AIR_CONDITIONING",
"mode": "FAN",
"power": "ON"
},
"type": "MANUAL"
},
"activityDataPoints": {
"acPower": {
"timestamp": "2020-03-05T04:03:44.328Z",
"type": "POWER",
"value": "ON"
}
},
"nextTimeBlock": {
"start": "2020-03-05T08:00:00.000Z"
},
"preparation": null,
"overlayType": "MANUAL",
"nextScheduleChange": null,
"setting": {
"type": "AIR_CONDITIONING",
"mode": "FAN",
"power": "ON"
}
}

View file

@ -1,67 +0,0 @@
{
"tadoMode": "HOME",
"sensorDataPoints": {
"insideTemperature": {
"fahrenheit": 76.57,
"timestamp": "2020-03-05T03:57:38.850Z",
"celsius": 24.76,
"type": "TEMPERATURE",
"precision": {
"fahrenheit": 0.1,
"celsius": 0.1
}
},
"humidity": {
"timestamp": "2020-03-05T03:57:38.850Z",
"percentage": 60.9,
"type": "PERCENTAGE"
}
},
"link": {
"state": "ONLINE"
},
"openWindow": null,
"geolocationOverride": false,
"geolocationOverrideDisableTime": null,
"overlay": {
"termination": {
"typeSkillBasedApp": "TADO_MODE",
"projectedExpiry": null,
"type": "TADO_MODE"
},
"setting": {
"fanSpeed": "AUTO",
"type": "AIR_CONDITIONING",
"mode": "HEAT",
"power": "ON",
"temperature": {
"fahrenheit": 61.0,
"celsius": 16.11
}
},
"type": "MANUAL"
},
"activityDataPoints": {
"acPower": {
"timestamp": "2020-03-05T03:59:36.390Z",
"type": "POWER",
"value": "ON"
}
},
"nextTimeBlock": {
"start": "2020-03-05T08:00:00.000Z"
},
"preparation": null,
"overlayType": "MANUAL",
"nextScheduleChange": null,
"setting": {
"fanSpeed": "AUTO",
"type": "AIR_CONDITIONING",
"mode": "HEAT",
"power": "ON",
"temperature": {
"fahrenheit": 61.0,
"celsius": 16.11
}
}
}

View file

@ -1,55 +0,0 @@
{
"tadoMode": "AWAY",
"sensorDataPoints": {
"insideTemperature": {
"fahrenheit": 70.59,
"timestamp": "2020-03-05T01:21:44.089Z",
"celsius": 21.44,
"type": "TEMPERATURE",
"precision": {
"fahrenheit": 0.1,
"celsius": 0.1
}
},
"humidity": {
"timestamp": "2020-03-05T01:21:44.089Z",
"percentage": 48.2,
"type": "PERCENTAGE"
}
},
"link": {
"state": "ONLINE"
},
"openWindow": null,
"geolocationOverride": false,
"geolocationOverrideDisableTime": null,
"overlay": {
"termination": {
"typeSkillBasedApp": "MANUAL",
"projectedExpiry": null,
"type": "MANUAL"
},
"setting": {
"type": "AIR_CONDITIONING",
"power": "OFF"
},
"type": "MANUAL"
},
"activityDataPoints": {
"acPower": {
"timestamp": "2020-02-29T05:34:10.318Z",
"type": "POWER",
"value": "OFF"
}
},
"nextTimeBlock": {
"start": "2020-03-05T04:00:00.000Z"
},
"preparation": null,
"overlayType": "MANUAL",
"nextScheduleChange": null,
"setting": {
"type": "AIR_CONDITIONING",
"power": "OFF"
}
}

View file

@ -1,55 +0,0 @@
{
"tadoMode": "HOME",
"sensorDataPoints": {
"insideTemperature": {
"fahrenheit": 77.02,
"timestamp": "2020-03-05T04:02:07.396Z",
"celsius": 25.01,
"type": "TEMPERATURE",
"precision": {
"fahrenheit": 0.1,
"celsius": 0.1
}
},
"humidity": {
"timestamp": "2020-03-05T04:02:07.396Z",
"percentage": 62.0,
"type": "PERCENTAGE"
}
},
"link": {
"state": "ONLINE"
},
"openWindow": null,
"geolocationOverride": false,
"geolocationOverrideDisableTime": null,
"overlay": {
"termination": {
"typeSkillBasedApp": "MANUAL",
"projectedExpiry": null,
"type": "MANUAL"
},
"setting": {
"type": "AIR_CONDITIONING",
"power": "OFF"
},
"type": "MANUAL"
},
"activityDataPoints": {
"acPower": {
"timestamp": "2020-03-05T04:05:08.804Z",
"type": "POWER",
"value": "OFF"
}
},
"nextTimeBlock": {
"start": "2020-03-05T08:00:00.000Z"
},
"preparation": null,
"overlayType": "MANUAL",
"nextScheduleChange": null,
"setting": {
"type": "AIR_CONDITIONING",
"power": "OFF"
}
}

View file

@ -1,71 +0,0 @@
{
"tadoMode": "HOME",
"sensorDataPoints": {
"insideTemperature": {
"fahrenheit": 77.09,
"timestamp": "2020-03-03T21:23:57.846Z",
"celsius": 25.05,
"type": "TEMPERATURE",
"precision": {
"fahrenheit": 0.1,
"celsius": 0.1
}
},
"humidity": {
"timestamp": "2020-03-03T21:23:57.846Z",
"percentage": 61.6,
"type": "PERCENTAGE"
}
},
"link": {
"state": "OFFLINE",
"reason": {
"code": "disconnectedDevice",
"title": "There is a disconnected device."
}
},
"openWindow": null,
"geolocationOverride": false,
"geolocationOverrideDisableTime": null,
"overlay": {
"termination": {
"typeSkillBasedApp": "TADO_MODE",
"projectedExpiry": null,
"type": "TADO_MODE"
},
"setting": {
"fanSpeed": "AUTO",
"type": "AIR_CONDITIONING",
"mode": "COOL",
"power": "ON",
"temperature": {
"fahrenheit": 64.0,
"celsius": 17.78
}
},
"type": "MANUAL"
},
"activityDataPoints": {
"acPower": {
"timestamp": "2020-02-29T18:42:26.683Z",
"type": "POWER",
"value": "OFF"
}
},
"nextTimeBlock": {
"start": "2020-03-05T08:00:00.000Z"
},
"preparation": null,
"overlayType": "MANUAL",
"nextScheduleChange": null,
"setting": {
"fanSpeed": "AUTO",
"type": "AIR_CONDITIONING",
"mode": "COOL",
"power": "ON",
"temperature": {
"fahrenheit": 64.0,
"celsius": 17.78
}
}
}

View file

@ -1,50 +0,0 @@
{
"tadoMode": "HOME",
"sensorDataPoints": {
"insideTemperature": {
"fahrenheit": 75.97,
"timestamp": "2020-03-05T03:50:24.769Z",
"celsius": 24.43,
"type": "TEMPERATURE",
"precision": {
"fahrenheit": 0.1,
"celsius": 0.1
}
},
"humidity": {
"timestamp": "2020-03-05T03:50:24.769Z",
"percentage": 60.0,
"type": "PERCENTAGE"
}
},
"link": {
"state": "ONLINE"
},
"openWindow": null,
"geolocationOverride": false,
"geolocationOverrideDisableTime": null,
"overlay": null,
"activityDataPoints": {
"acPower": {
"timestamp": "2020-03-05T03:52:22.253Z",
"type": "POWER",
"value": "OFF"
}
},
"nextTimeBlock": {
"start": "2020-03-05T08:00:00.000Z"
},
"preparation": null,
"overlayType": null,
"nextScheduleChange": null,
"setting": {
"fanSpeed": "MIDDLE",
"type": "AIR_CONDITIONING",
"mode": "COOL",
"power": "ON",
"temperature": {
"fahrenheit": 68.0,
"celsius": 20.0
}
}
}

View file

@ -1,55 +0,0 @@
{
"tadoMode": "HOME",
"geolocationOverride": false,
"geolocationOverrideDisableTime": null,
"preparation": null,
"setting": {
"type": "AIR_CONDITIONING",
"power": "OFF"
},
"overlayType": "MANUAL",
"overlay": {
"type": "MANUAL",
"setting": {
"type": "AIR_CONDITIONING",
"power": "OFF"
},
"termination": {
"type": "MANUAL",
"typeSkillBasedApp": "MANUAL",
"projectedExpiry": null
}
},
"openWindow": null,
"nextScheduleChange": null,
"nextTimeBlock": {
"start": "2020-03-07T04:00:00.000Z"
},
"link": {
"state": "ONLINE"
},
"activityDataPoints": {
"acPower": {
"timestamp": "2020-03-06T19:05:21.835Z",
"type": "POWER",
"value": "ON"
}
},
"sensorDataPoints": {
"insideTemperature": {
"celsius": 21.40,
"fahrenheit": 70.52,
"timestamp": "2020-03-06T19:06:13.185Z",
"type": "TEMPERATURE",
"precision": {
"celsius": 0.1,
"fahrenheit": 0.1
}
},
"humidity": {
"type": "PERCENTAGE",
"percentage": 49.20,
"timestamp": "2020-03-06T19:06:13.185Z"
}
}
}