Rework tado component (#29246)
* Fix imports so it works in custom_components * Rework tado component * Code cleanup * Remove water_heater * Address pylint warnings * Remove water_heater from components * Raise PlatformNotReady when we couldn't connect * Revert PlatformNotReady since we are not a platform * Add debugging information * Add fallback setting * Import with relative path * Address race condition * Cleanup * Catch 422 Errors and log the real error * Use async_schedule_update_ha_state to update the entities * Forgot the True
This commit is contained in:
parent
92fd3e3ad5
commit
04b5d6c697
4 changed files with 345 additions and 420 deletions
|
@ -9,23 +9,29 @@ import voluptuous as vol
|
||||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||||
from homeassistant.helpers import config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.discovery import load_platform
|
from homeassistant.helpers.discovery import load_platform
|
||||||
|
from homeassistant.helpers.dispatcher import dispatcher_send
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
|
|
||||||
|
from .const import CONF_FALLBACK
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
DATA_TADO = "tado_data"
|
|
||||||
DOMAIN = "tado"
|
DOMAIN = "tado"
|
||||||
|
|
||||||
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=10)
|
SIGNAL_TADO_UPDATE_RECEIVED = "tado_update_received_{}_{}"
|
||||||
|
|
||||||
TADO_COMPONENTS = ["sensor", "climate"]
|
TADO_COMPONENTS = ["sensor", "climate"]
|
||||||
|
|
||||||
|
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=10)
|
||||||
|
SCAN_INTERVAL = timedelta(seconds=15)
|
||||||
|
|
||||||
CONFIG_SCHEMA = vol.Schema(
|
CONFIG_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
DOMAIN: vol.Schema(
|
DOMAIN: vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_USERNAME): cv.string,
|
vol.Required(CONF_USERNAME): cv.string,
|
||||||
vol.Required(CONF_PASSWORD): cv.string,
|
vol.Required(CONF_PASSWORD): cv.string,
|
||||||
|
vol.Optional(CONF_FALLBACK, default=True): cv.boolean,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -38,91 +44,106 @@ def setup(hass, config):
|
||||||
username = config[DOMAIN][CONF_USERNAME]
|
username = config[DOMAIN][CONF_USERNAME]
|
||||||
password = config[DOMAIN][CONF_PASSWORD]
|
password = config[DOMAIN][CONF_PASSWORD]
|
||||||
|
|
||||||
try:
|
tadoconnector = TadoConnector(hass, username, password)
|
||||||
tado = Tado(username, password)
|
if not tadoconnector.setup():
|
||||||
tado.setDebugging(True)
|
|
||||||
except (RuntimeError, urllib.error.HTTPError):
|
|
||||||
_LOGGER.error("Unable to connect to mytado with username and password")
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
hass.data[DATA_TADO] = TadoDataStore(tado)
|
hass.data[DOMAIN] = tadoconnector
|
||||||
|
|
||||||
|
# Do first update
|
||||||
|
tadoconnector.update()
|
||||||
|
|
||||||
|
# Load components
|
||||||
for component in TADO_COMPONENTS:
|
for component in TADO_COMPONENTS:
|
||||||
load_platform(hass, component, DOMAIN, {}, config)
|
load_platform(
|
||||||
|
hass,
|
||||||
|
component,
|
||||||
|
DOMAIN,
|
||||||
|
{CONF_FALLBACK: config[DOMAIN][CONF_FALLBACK]},
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Poll for updates in the background
|
||||||
|
hass.helpers.event.track_time_interval(
|
||||||
|
lambda now: tadoconnector.update(), SCAN_INTERVAL
|
||||||
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class TadoDataStore:
|
class TadoConnector:
|
||||||
"""An object to store the Tado data."""
|
"""An object to store the Tado data."""
|
||||||
|
|
||||||
def __init__(self, tado):
|
def __init__(self, hass, username, password):
|
||||||
"""Initialize Tado data store."""
|
"""Initialize Tado Connector."""
|
||||||
self.tado = tado
|
self.hass = hass
|
||||||
|
self._username = username
|
||||||
|
self._password = password
|
||||||
|
|
||||||
self.sensors = {}
|
self.tado = None
|
||||||
self.data = {}
|
self.zones = None
|
||||||
|
self.devices = None
|
||||||
|
self.data = {
|
||||||
|
"zone": {},
|
||||||
|
"device": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
"""Connect to Tado and fetch the zones."""
|
||||||
|
try:
|
||||||
|
self.tado = Tado(self._username, self._password)
|
||||||
|
except (RuntimeError, urllib.error.HTTPError) as exc:
|
||||||
|
_LOGGER.error("Unable to connect: %s", exc)
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.tado.setDebugging(True)
|
||||||
|
|
||||||
|
# Load zones and devices
|
||||||
|
self.zones = self.tado.getZones()
|
||||||
|
self.devices = self.tado.getMe()["homes"]
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Update the internal data from mytado.com."""
|
"""Update the registered zones."""
|
||||||
for data_id, sensor in list(self.sensors.items()):
|
for zone in self.zones:
|
||||||
data = None
|
self.update_sensor("zone", zone["id"])
|
||||||
|
for device in self.devices:
|
||||||
|
self.update_sensor("device", device["id"])
|
||||||
|
|
||||||
try:
|
def update_sensor(self, sensor_type, sensor):
|
||||||
if "zone" in sensor:
|
"""Update the internal data from Tado."""
|
||||||
_LOGGER.debug(
|
_LOGGER.debug("Updating %s %s", sensor_type, sensor)
|
||||||
"Querying mytado.com for zone %s %s",
|
try:
|
||||||
sensor["id"],
|
if sensor_type == "zone":
|
||||||
sensor["name"],
|
data = self.tado.getState(sensor)
|
||||||
)
|
elif sensor_type == "device":
|
||||||
data = self.tado.getState(sensor["id"])
|
data = self.tado.getDevices()[0]
|
||||||
|
else:
|
||||||
|
_LOGGER.debug("Unknown sensor: %s", sensor_type)
|
||||||
|
return
|
||||||
|
except RuntimeError:
|
||||||
|
_LOGGER.error(
|
||||||
|
"Unable to connect to Tado while updating %s %s", sensor_type, sensor,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
if "device" in sensor:
|
self.data[sensor_type][sensor] = data
|
||||||
_LOGGER.debug(
|
|
||||||
"Querying mytado.com for device %s %s",
|
|
||||||
sensor["id"],
|
|
||||||
sensor["name"],
|
|
||||||
)
|
|
||||||
data = self.tado.getDevices()[0]
|
|
||||||
|
|
||||||
except RuntimeError:
|
_LOGGER.debug("Dispatching update to %s %s: %s", sensor_type, sensor, data)
|
||||||
_LOGGER.error(
|
dispatcher_send(
|
||||||
"Unable to connect to myTado. %s %s", sensor["id"], sensor["id"]
|
self.hass, SIGNAL_TADO_UPDATE_RECEIVED.format(sensor_type, sensor)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.data[data_id] = data
|
def get_capabilities(self, zone_id):
|
||||||
|
"""Return the capabilities of the devices."""
|
||||||
def add_sensor(self, data_id, sensor):
|
return self.tado.getCapabilities(zone_id)
|
||||||
"""Add a sensor to update in _update()."""
|
|
||||||
self.sensors[data_id] = sensor
|
|
||||||
self.data[data_id] = None
|
|
||||||
|
|
||||||
def get_data(self, data_id):
|
|
||||||
"""Get the cached data."""
|
|
||||||
data = {"error": "no data"}
|
|
||||||
|
|
||||||
if data_id in self.data:
|
|
||||||
data = self.data[data_id]
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
def get_zones(self):
|
|
||||||
"""Wrap for getZones()."""
|
|
||||||
return self.tado.getZones()
|
|
||||||
|
|
||||||
def get_capabilities(self, tado_id):
|
|
||||||
"""Wrap for getCapabilities(..)."""
|
|
||||||
return self.tado.getCapabilities(tado_id)
|
|
||||||
|
|
||||||
def get_me(self):
|
|
||||||
"""Wrap for getMe()."""
|
|
||||||
return self.tado.getMe()
|
|
||||||
|
|
||||||
def reset_zone_overlay(self, zone_id):
|
def reset_zone_overlay(self, zone_id):
|
||||||
"""Wrap for resetZoneOverlay(..)."""
|
"""Reset the zone back to the default operation."""
|
||||||
self.tado.resetZoneOverlay(zone_id)
|
self.tado.resetZoneOverlay(zone_id)
|
||||||
self.update(no_throttle=True) # pylint: disable=unexpected-keyword-arg
|
self.update_sensor("zone", zone_id)
|
||||||
|
|
||||||
def set_zone_overlay(
|
def set_zone_overlay(
|
||||||
self,
|
self,
|
||||||
|
@ -133,13 +154,32 @@ class TadoDataStore:
|
||||||
device_type="HEATING",
|
device_type="HEATING",
|
||||||
mode=None,
|
mode=None,
|
||||||
):
|
):
|
||||||
"""Wrap for setZoneOverlay(..)."""
|
"""Set a zone overlay."""
|
||||||
self.tado.setZoneOverlay(
|
_LOGGER.debug(
|
||||||
zone_id, overlay_mode, temperature, duration, device_type, "ON", mode
|
"Set overlay for zone %s: mode=%s, temp=%s, duration=%s, type=%s, mode=%s",
|
||||||
|
zone_id,
|
||||||
|
overlay_mode,
|
||||||
|
temperature,
|
||||||
|
duration,
|
||||||
|
device_type,
|
||||||
|
mode,
|
||||||
)
|
)
|
||||||
self.update(no_throttle=True) # pylint: disable=unexpected-keyword-arg
|
try:
|
||||||
|
self.tado.setZoneOverlay(
|
||||||
|
zone_id, overlay_mode, temperature, duration, device_type, "ON", mode
|
||||||
|
)
|
||||||
|
except urllib.error.HTTPError as exc:
|
||||||
|
_LOGGER.error("Could not set zone overlay: %s", exc.read())
|
||||||
|
|
||||||
|
self.update_sensor("zone", zone_id)
|
||||||
|
|
||||||
def set_zone_off(self, zone_id, overlay_mode, device_type="HEATING"):
|
def set_zone_off(self, zone_id, overlay_mode, device_type="HEATING"):
|
||||||
"""Set a zone to off."""
|
"""Set a zone to off."""
|
||||||
self.tado.setZoneOverlay(zone_id, overlay_mode, None, None, device_type, "OFF")
|
try:
|
||||||
self.update(no_throttle=True) # pylint: disable=unexpected-keyword-arg
|
self.tado.setZoneOverlay(
|
||||||
|
zone_id, overlay_mode, None, None, device_type, "OFF"
|
||||||
|
)
|
||||||
|
except urllib.error.HTTPError as exc:
|
||||||
|
_LOGGER.error("Could not set zone overlay: %s", exc.read())
|
||||||
|
|
||||||
|
self.update_sensor("zone", zone_id)
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
"""Support for Tado to create a climate device for each zone."""
|
"""Support for Tado thermostats."""
|
||||||
import logging
|
import logging
|
||||||
from typing import List, Optional
|
|
||||||
|
|
||||||
from homeassistant.components.climate import ClimateDevice
|
from homeassistant.components.climate import ClimateDevice
|
||||||
from homeassistant.components.climate.const import (
|
from homeassistant.components.climate.const import (
|
||||||
|
@ -23,27 +22,20 @@ from homeassistant.components.climate.const import (
|
||||||
SUPPORT_TARGET_TEMPERATURE,
|
SUPPORT_TARGET_TEMPERATURE,
|
||||||
)
|
)
|
||||||
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_TENTHS, TEMP_CELSIUS
|
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_TENTHS, TEMP_CELSIUS
|
||||||
from homeassistant.util.temperature import convert as convert_temperature
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
|
|
||||||
from . import DATA_TADO
|
from . import CONF_FALLBACK, DOMAIN, SIGNAL_TADO_UPDATE_RECEIVED
|
||||||
|
from .const import (
|
||||||
|
CONST_MODE_OFF,
|
||||||
|
CONST_MODE_SMART_SCHEDULE,
|
||||||
|
CONST_OVERLAY_MANUAL,
|
||||||
|
CONST_OVERLAY_TADO_MODE,
|
||||||
|
TYPE_AIR_CONDITIONING,
|
||||||
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
CONST_MODE_SMART_SCHEDULE = "SMART_SCHEDULE" # Default mytado mode
|
|
||||||
CONST_MODE_OFF = "OFF" # Switch off heating in a zone
|
|
||||||
|
|
||||||
# When we change the temperature setting, we need an overlay mode
|
|
||||||
# wait until tado changes the mode automatic
|
|
||||||
CONST_OVERLAY_TADO_MODE = "TADO_MODE"
|
|
||||||
# the user has change the temperature or mode manually
|
|
||||||
CONST_OVERLAY_MANUAL = "MANUAL"
|
|
||||||
# the temperature will be reset after a timespan
|
|
||||||
CONST_OVERLAY_TIMER = "TIMER"
|
|
||||||
|
|
||||||
CONST_MODE_FAN_HIGH = "HIGH"
|
|
||||||
CONST_MODE_FAN_MIDDLE = "MIDDLE"
|
|
||||||
CONST_MODE_FAN_LOW = "LOW"
|
|
||||||
|
|
||||||
FAN_MAP_TADO = {"HIGH": FAN_HIGH, "MIDDLE": FAN_MIDDLE, "LOW": FAN_LOW}
|
FAN_MAP_TADO = {"HIGH": FAN_HIGH, "MIDDLE": FAN_MIDDLE, "LOW": FAN_LOW}
|
||||||
|
|
||||||
HVAC_MAP_TADO_HEAT = {
|
HVAC_MAP_TADO_HEAT = {
|
||||||
|
@ -78,35 +70,29 @@ SUPPORT_PRESET = [PRESET_AWAY, PRESET_HOME]
|
||||||
|
|
||||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||||
"""Set up the Tado climate platform."""
|
"""Set up the Tado climate platform."""
|
||||||
tado = hass.data[DATA_TADO]
|
tado = hass.data[DOMAIN]
|
||||||
|
|
||||||
try:
|
entities = []
|
||||||
zones = tado.get_zones()
|
for zone in tado.zones:
|
||||||
except RuntimeError:
|
entity = create_climate_entity(
|
||||||
_LOGGER.error("Unable to get zone info from mytado")
|
tado, zone["name"], zone["id"], discovery_info[CONF_FALLBACK]
|
||||||
return
|
)
|
||||||
|
if entity:
|
||||||
|
entities.append(entity)
|
||||||
|
|
||||||
climate_devices = []
|
if entities:
|
||||||
for zone in zones:
|
add_entities(entities, True)
|
||||||
device = create_climate_device(tado, hass, zone, zone["name"], zone["id"])
|
|
||||||
if not device:
|
|
||||||
continue
|
|
||||||
climate_devices.append(device)
|
|
||||||
|
|
||||||
if climate_devices:
|
|
||||||
add_entities(climate_devices, True)
|
|
||||||
|
|
||||||
|
|
||||||
def create_climate_device(tado, hass, zone, name, zone_id):
|
def create_climate_entity(tado, name: str, zone_id: int, fallback: bool):
|
||||||
"""Create a Tado climate device."""
|
"""Create a Tado climate entity."""
|
||||||
capabilities = tado.get_capabilities(zone_id)
|
capabilities = tado.get_capabilities(zone_id)
|
||||||
|
_LOGGER.debug("Capabilities for zone %s: %s", zone_id, capabilities)
|
||||||
|
|
||||||
|
zone_type = capabilities["type"]
|
||||||
|
|
||||||
unit = TEMP_CELSIUS
|
|
||||||
ac_device = capabilities["type"] == "AIR_CONDITIONING"
|
|
||||||
hot_water_device = capabilities["type"] == "HOT_WATER"
|
|
||||||
ac_support_heat = False
|
ac_support_heat = False
|
||||||
|
if zone_type == TYPE_AIR_CONDITIONING:
|
||||||
if ac_device:
|
|
||||||
# Only use heat if available
|
# Only use heat if available
|
||||||
# (you don't have to setup a heat mode, but cool is required)
|
# (you don't have to setup a heat mode, but cool is required)
|
||||||
# Heat is preferred as it generally has a lower minimum temperature
|
# Heat is preferred as it generally has a lower minimum temperature
|
||||||
|
@ -118,67 +104,56 @@ def create_climate_device(tado, hass, zone, name, zone_id):
|
||||||
elif "temperatures" in capabilities:
|
elif "temperatures" in capabilities:
|
||||||
temperatures = capabilities["temperatures"]
|
temperatures = capabilities["temperatures"]
|
||||||
else:
|
else:
|
||||||
_LOGGER.debug("Received zone %s has no temperature; not adding", name)
|
_LOGGER.debug("Not adding zone %s since it has no temperature", name)
|
||||||
return
|
return None
|
||||||
|
|
||||||
min_temp = float(temperatures["celsius"]["min"])
|
min_temp = float(temperatures["celsius"]["min"])
|
||||||
max_temp = float(temperatures["celsius"]["max"])
|
max_temp = float(temperatures["celsius"]["max"])
|
||||||
step = temperatures["celsius"].get("step", PRECISION_TENTHS)
|
step = temperatures["celsius"].get("step", PRECISION_TENTHS)
|
||||||
|
|
||||||
data_id = f"zone {name} {zone_id}"
|
entity = TadoClimate(
|
||||||
device = TadoClimate(
|
|
||||||
tado,
|
tado,
|
||||||
name,
|
name,
|
||||||
zone_id,
|
zone_id,
|
||||||
data_id,
|
zone_type,
|
||||||
hass.config.units.temperature(min_temp, unit),
|
|
||||||
hass.config.units.temperature(max_temp, unit),
|
|
||||||
step,
|
|
||||||
ac_device,
|
|
||||||
hot_water_device,
|
|
||||||
ac_support_heat,
|
|
||||||
)
|
|
||||||
|
|
||||||
tado.add_sensor(
|
|
||||||
data_id, {"id": zone_id, "zone": zone, "name": name, "climate": device}
|
|
||||||
)
|
|
||||||
|
|
||||||
return device
|
|
||||||
|
|
||||||
|
|
||||||
class TadoClimate(ClimateDevice):
|
|
||||||
"""Representation of a Tado climate device."""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
store,
|
|
||||||
zone_name,
|
|
||||||
zone_id,
|
|
||||||
data_id,
|
|
||||||
min_temp,
|
min_temp,
|
||||||
max_temp,
|
max_temp,
|
||||||
step,
|
step,
|
||||||
ac_device,
|
|
||||||
hot_water_device,
|
|
||||||
ac_support_heat,
|
ac_support_heat,
|
||||||
tolerance=0.3,
|
fallback,
|
||||||
|
)
|
||||||
|
return entity
|
||||||
|
|
||||||
|
|
||||||
|
class TadoClimate(ClimateDevice):
|
||||||
|
"""Representation of a Tado climate entity."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
tado,
|
||||||
|
zone_name,
|
||||||
|
zone_id,
|
||||||
|
zone_type,
|
||||||
|
min_temp,
|
||||||
|
max_temp,
|
||||||
|
step,
|
||||||
|
ac_support_heat,
|
||||||
|
fallback,
|
||||||
):
|
):
|
||||||
"""Initialize of Tado climate device."""
|
"""Initialize of Tado climate entity."""
|
||||||
self._store = store
|
self._tado = tado
|
||||||
self._data_id = data_id
|
|
||||||
|
|
||||||
self.zone_name = zone_name
|
self.zone_name = zone_name
|
||||||
self.zone_id = zone_id
|
self.zone_id = zone_id
|
||||||
|
self.zone_type = zone_type
|
||||||
|
|
||||||
self._ac_device = ac_device
|
self._ac_device = zone_type == TYPE_AIR_CONDITIONING
|
||||||
self._hot_water_device = hot_water_device
|
|
||||||
self._ac_support_heat = ac_support_heat
|
self._ac_support_heat = ac_support_heat
|
||||||
self._cooling = False
|
self._cooling = False
|
||||||
|
|
||||||
self._active = False
|
self._active = False
|
||||||
self._device_is_active = False
|
self._device_is_active = False
|
||||||
|
|
||||||
self._unit = TEMP_CELSIUS
|
|
||||||
self._cur_temp = None
|
self._cur_temp = None
|
||||||
self._cur_humidity = None
|
self._cur_humidity = None
|
||||||
self._is_away = False
|
self._is_away = False
|
||||||
|
@ -186,12 +161,34 @@ class TadoClimate(ClimateDevice):
|
||||||
self._max_temp = max_temp
|
self._max_temp = max_temp
|
||||||
self._step = step
|
self._step = step
|
||||||
self._target_temp = None
|
self._target_temp = None
|
||||||
self._tolerance = tolerance
|
|
||||||
|
if fallback:
|
||||||
|
_LOGGER.debug("Default overlay is set to TADO MODE")
|
||||||
|
# Fallback to Smart Schedule at next Schedule switch
|
||||||
|
self._default_overlay = CONST_OVERLAY_TADO_MODE
|
||||||
|
else:
|
||||||
|
_LOGGER.debug("Default overlay is set to MANUAL MODE")
|
||||||
|
# Don't fallback to Smart Schedule, but keep in manual mode
|
||||||
|
self._default_overlay = CONST_OVERLAY_MANUAL
|
||||||
|
|
||||||
self._current_fan = CONST_MODE_OFF
|
self._current_fan = CONST_MODE_OFF
|
||||||
self._current_operation = CONST_MODE_SMART_SCHEDULE
|
self._current_operation = CONST_MODE_SMART_SCHEDULE
|
||||||
self._overlay_mode = 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),
|
||||||
|
async_update_callback,
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_features(self):
|
def supported_features(self):
|
||||||
"""Return the list of supported features."""
|
"""Return the list of supported features."""
|
||||||
|
@ -199,18 +196,19 @@ class TadoClimate(ClimateDevice):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""Return the name of the device."""
|
"""Return the name of the entity."""
|
||||||
return self.zone_name
|
return self.zone_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def should_poll(self) -> bool:
|
||||||
|
"""Do not poll."""
|
||||||
|
return False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def current_humidity(self):
|
def current_humidity(self):
|
||||||
"""Return the current humidity."""
|
"""Return the current humidity."""
|
||||||
return self._cur_humidity
|
return self._cur_humidity
|
||||||
|
|
||||||
def set_humidity(self, humidity: int) -> None:
|
|
||||||
"""Set new target humidity."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def current_temperature(self):
|
def current_temperature(self):
|
||||||
"""Return the sensor temperature."""
|
"""Return the sensor temperature."""
|
||||||
|
@ -234,9 +232,9 @@ class TadoClimate(ClimateDevice):
|
||||||
|
|
||||||
Need to be a subset of HVAC_MODES.
|
Need to be a subset of HVAC_MODES.
|
||||||
"""
|
"""
|
||||||
if self._ac_device and self._ac_support_heat:
|
if self._ac_device:
|
||||||
return SUPPORT_HVAC_HEAT_COOL
|
if self._ac_support_heat:
|
||||||
if self._ac_device and not self._ac_support_heat:
|
return SUPPORT_HVAC_HEAT_COOL
|
||||||
return SUPPORT_HVAC_COOL
|
return SUPPORT_HVAC_COOL
|
||||||
return SUPPORT_HVAC_HEAT
|
return SUPPORT_HVAC_HEAT
|
||||||
|
|
||||||
|
@ -248,16 +246,10 @@ class TadoClimate(ClimateDevice):
|
||||||
"""
|
"""
|
||||||
if not self._device_is_active:
|
if not self._device_is_active:
|
||||||
return CURRENT_HVAC_OFF
|
return CURRENT_HVAC_OFF
|
||||||
if self._ac_device and self._ac_support_heat and self._cooling:
|
if self._ac_device:
|
||||||
if self._active:
|
|
||||||
return CURRENT_HVAC_COOL
|
|
||||||
return CURRENT_HVAC_IDLE
|
|
||||||
if self._ac_device and self._ac_support_heat and not self._cooling:
|
|
||||||
if self._active:
|
|
||||||
return CURRENT_HVAC_HEAT
|
|
||||||
return CURRENT_HVAC_IDLE
|
|
||||||
if self._ac_device and not self._ac_support_heat:
|
|
||||||
if self._active:
|
if self._active:
|
||||||
|
if self._ac_support_heat and not self._cooling:
|
||||||
|
return CURRENT_HVAC_HEAT
|
||||||
return CURRENT_HVAC_COOL
|
return CURRENT_HVAC_COOL
|
||||||
return CURRENT_HVAC_IDLE
|
return CURRENT_HVAC_IDLE
|
||||||
if self._active:
|
if self._active:
|
||||||
|
@ -284,7 +276,7 @@ class TadoClimate(ClimateDevice):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def preset_mode(self):
|
def preset_mode(self):
|
||||||
"""Return the current preset mode, e.g., home, away, temp."""
|
"""Return the current preset mode (home, away)."""
|
||||||
if self._is_away:
|
if self._is_away:
|
||||||
return PRESET_AWAY
|
return PRESET_AWAY
|
||||||
return PRESET_HOME
|
return PRESET_HOME
|
||||||
|
@ -301,7 +293,7 @@ class TadoClimate(ClimateDevice):
|
||||||
@property
|
@property
|
||||||
def temperature_unit(self):
|
def temperature_unit(self):
|
||||||
"""Return the unit of measurement used by the platform."""
|
"""Return the unit of measurement used by the platform."""
|
||||||
return self._unit
|
return TEMP_CELSIUS
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def target_temperature_step(self):
|
def target_temperature_step(self):
|
||||||
|
@ -313,23 +305,13 @@ class TadoClimate(ClimateDevice):
|
||||||
"""Return the temperature we try to reach."""
|
"""Return the temperature we try to reach."""
|
||||||
return self._target_temp
|
return self._target_temp
|
||||||
|
|
||||||
@property
|
|
||||||
def target_temperature_high(self):
|
|
||||||
"""Return the upper bound temperature we try to reach."""
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def target_temperature_low(self):
|
|
||||||
"""Return the lower bound temperature we try to reach."""
|
|
||||||
return None
|
|
||||||
|
|
||||||
def set_temperature(self, **kwargs):
|
def set_temperature(self, **kwargs):
|
||||||
"""Set new target temperature."""
|
"""Set new target temperature."""
|
||||||
temperature = kwargs.get(ATTR_TEMPERATURE)
|
temperature = kwargs.get(ATTR_TEMPERATURE)
|
||||||
if temperature is None:
|
if temperature is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
self._current_operation = CONST_OVERLAY_TADO_MODE
|
self._current_operation = self._default_overlay
|
||||||
self._overlay_mode = None
|
self._overlay_mode = None
|
||||||
self._target_temp = temperature
|
self._target_temp = temperature
|
||||||
self._control_heating()
|
self._control_heating()
|
||||||
|
@ -343,50 +325,51 @@ class TadoClimate(ClimateDevice):
|
||||||
elif hvac_mode == HVAC_MODE_AUTO:
|
elif hvac_mode == HVAC_MODE_AUTO:
|
||||||
mode = CONST_MODE_SMART_SCHEDULE
|
mode = CONST_MODE_SMART_SCHEDULE
|
||||||
elif hvac_mode == HVAC_MODE_HEAT:
|
elif hvac_mode == HVAC_MODE_HEAT:
|
||||||
mode = CONST_OVERLAY_TADO_MODE
|
mode = self._default_overlay
|
||||||
elif hvac_mode == HVAC_MODE_COOL:
|
elif hvac_mode == HVAC_MODE_COOL:
|
||||||
mode = CONST_OVERLAY_TADO_MODE
|
mode = self._default_overlay
|
||||||
elif hvac_mode == HVAC_MODE_HEAT_COOL:
|
elif hvac_mode == HVAC_MODE_HEAT_COOL:
|
||||||
mode = CONST_OVERLAY_TADO_MODE
|
mode = self._default_overlay
|
||||||
|
|
||||||
self._current_operation = mode
|
self._current_operation = mode
|
||||||
self._overlay_mode = None
|
self._overlay_mode = None
|
||||||
if self._target_temp is None and self._ac_device:
|
|
||||||
self._target_temp = 27
|
# 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()
|
self._control_heating()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def min_temp(self):
|
def min_temp(self):
|
||||||
"""Return the minimum temperature."""
|
"""Return the minimum temperature."""
|
||||||
return convert_temperature(
|
return self._min_temp
|
||||||
self._min_temp, self._unit, self.hass.config.units.temperature_unit
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def max_temp(self):
|
def max_temp(self):
|
||||||
"""Return the maximum temperature."""
|
"""Return the maximum temperature."""
|
||||||
return convert_temperature(
|
return self._max_temp
|
||||||
self._max_temp, self._unit, self.hass.config.units.temperature_unit
|
|
||||||
)
|
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Update the state of this climate device."""
|
"""Handle update callbacks."""
|
||||||
self._store.update()
|
_LOGGER.debug("Updating climate platform for zone %d", self.zone_id)
|
||||||
|
try:
|
||||||
data = self._store.get_data(self._data_id)
|
data = self._tado.data["zone"][self.zone_id]
|
||||||
|
except KeyError:
|
||||||
if data is None:
|
_LOGGER.debug("No data")
|
||||||
_LOGGER.debug("Received no data for zone %s", self.zone_name)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if "sensorDataPoints" in data:
|
if "sensorDataPoints" in data:
|
||||||
sensor_data = data["sensorDataPoints"]
|
sensor_data = data["sensorDataPoints"]
|
||||||
|
|
||||||
unit = TEMP_CELSIUS
|
|
||||||
|
|
||||||
if "insideTemperature" in sensor_data:
|
if "insideTemperature" in sensor_data:
|
||||||
temperature = float(sensor_data["insideTemperature"]["celsius"])
|
temperature = float(sensor_data["insideTemperature"]["celsius"])
|
||||||
self._cur_temp = self.hass.config.units.temperature(temperature, unit)
|
self._cur_temp = temperature
|
||||||
|
|
||||||
if "humidity" in sensor_data:
|
if "humidity" in sensor_data:
|
||||||
humidity = float(sensor_data["humidity"]["percentage"])
|
humidity = float(sensor_data["humidity"]["percentage"])
|
||||||
|
@ -398,7 +381,7 @@ class TadoClimate(ClimateDevice):
|
||||||
and data["setting"]["temperature"] is not None
|
and data["setting"]["temperature"] is not None
|
||||||
):
|
):
|
||||||
setting = float(data["setting"]["temperature"]["celsius"])
|
setting = float(data["setting"]["temperature"]["celsius"])
|
||||||
self._target_temp = self.hass.config.units.temperature(setting, unit)
|
self._target_temp = setting
|
||||||
|
|
||||||
if "tadoMode" in data:
|
if "tadoMode" in data:
|
||||||
mode = data["tadoMode"]
|
mode = data["tadoMode"]
|
||||||
|
@ -468,135 +451,38 @@ class TadoClimate(ClimateDevice):
|
||||||
self._current_fan = fan_speed
|
self._current_fan = fan_speed
|
||||||
|
|
||||||
def _control_heating(self):
|
def _control_heating(self):
|
||||||
"""Send new target temperature to mytado."""
|
"""Send new target temperature to Tado."""
|
||||||
if None not in (self._cur_temp, self._target_temp):
|
|
||||||
_LOGGER.info(
|
|
||||||
"Obtained current (%d) and target temperature (%d). "
|
|
||||||
"Tado thermostat active",
|
|
||||||
self._cur_temp,
|
|
||||||
self._target_temp,
|
|
||||||
)
|
|
||||||
|
|
||||||
if self._current_operation == CONST_MODE_SMART_SCHEDULE:
|
if self._current_operation == CONST_MODE_SMART_SCHEDULE:
|
||||||
_LOGGER.info(
|
_LOGGER.debug(
|
||||||
"Switching mytado.com to SCHEDULE (default) for zone %s (%d)",
|
"Switching to SMART_SCHEDULE for zone %s (%d)",
|
||||||
self.zone_name,
|
self.zone_name,
|
||||||
self.zone_id,
|
self.zone_id,
|
||||||
)
|
)
|
||||||
self._store.reset_zone_overlay(self.zone_id)
|
self._tado.reset_zone_overlay(self.zone_id)
|
||||||
self._overlay_mode = self._current_operation
|
self._overlay_mode = self._current_operation
|
||||||
return
|
return
|
||||||
|
|
||||||
if self._current_operation == CONST_MODE_OFF:
|
if self._current_operation == CONST_MODE_OFF:
|
||||||
if self._ac_device:
|
_LOGGER.debug(
|
||||||
_LOGGER.info(
|
"Switching to OFF for zone %s (%d)", self.zone_name, self.zone_id
|
||||||
"Switching mytado.com to OFF for zone %s (%d) - AIR_CONDITIONING",
|
)
|
||||||
self.zone_name,
|
self._tado.set_zone_off(self.zone_id, CONST_OVERLAY_MANUAL, self.zone_type)
|
||||||
self.zone_id,
|
|
||||||
)
|
|
||||||
self._store.set_zone_off(
|
|
||||||
self.zone_id, CONST_OVERLAY_MANUAL, "AIR_CONDITIONING"
|
|
||||||
)
|
|
||||||
elif self._hot_water_device:
|
|
||||||
_LOGGER.info(
|
|
||||||
"Switching mytado.com to OFF for zone %s (%d) - HOT_WATER",
|
|
||||||
self.zone_name,
|
|
||||||
self.zone_id,
|
|
||||||
)
|
|
||||||
self._store.set_zone_off(
|
|
||||||
self.zone_id, CONST_OVERLAY_MANUAL, "HOT_WATER"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
_LOGGER.info(
|
|
||||||
"Switching mytado.com to OFF for zone %s (%d) - HEATING",
|
|
||||||
self.zone_name,
|
|
||||||
self.zone_id,
|
|
||||||
)
|
|
||||||
self._store.set_zone_off(self.zone_id, CONST_OVERLAY_MANUAL, "HEATING")
|
|
||||||
self._overlay_mode = self._current_operation
|
self._overlay_mode = self._current_operation
|
||||||
return
|
return
|
||||||
|
|
||||||
if self._ac_device:
|
_LOGGER.debug(
|
||||||
_LOGGER.info(
|
"Switching to %s for zone %s (%d) with temperature %s °C",
|
||||||
"Switching mytado.com to %s mode for zone %s (%d). Temp (%s) - AIR_CONDITIONING",
|
self._current_operation,
|
||||||
self._current_operation,
|
self.zone_name,
|
||||||
self.zone_name,
|
self.zone_id,
|
||||||
self.zone_id,
|
self._target_temp,
|
||||||
self._target_temp,
|
)
|
||||||
)
|
self._tado.set_zone_overlay(
|
||||||
self._store.set_zone_overlay(
|
self.zone_id,
|
||||||
self.zone_id,
|
self._current_operation,
|
||||||
self._current_operation,
|
self._target_temp,
|
||||||
self._target_temp,
|
None,
|
||||||
None,
|
self.zone_type,
|
||||||
"AIR_CONDITIONING",
|
"COOL" if self._ac_device else None,
|
||||||
"COOL",
|
)
|
||||||
)
|
|
||||||
elif self._hot_water_device:
|
|
||||||
_LOGGER.info(
|
|
||||||
"Switching mytado.com to %s mode for zone %s (%d). Temp (%s) - HOT_WATER",
|
|
||||||
self._current_operation,
|
|
||||||
self.zone_name,
|
|
||||||
self.zone_id,
|
|
||||||
self._target_temp,
|
|
||||||
)
|
|
||||||
self._store.set_zone_overlay(
|
|
||||||
self.zone_id,
|
|
||||||
self._current_operation,
|
|
||||||
self._target_temp,
|
|
||||||
None,
|
|
||||||
"HOT_WATER",
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
_LOGGER.info(
|
|
||||||
"Switching mytado.com to %s mode for zone %s (%d). Temp (%s) - HEATING",
|
|
||||||
self._current_operation,
|
|
||||||
self.zone_name,
|
|
||||||
self.zone_id,
|
|
||||||
self._target_temp,
|
|
||||||
)
|
|
||||||
self._store.set_zone_overlay(
|
|
||||||
self.zone_id,
|
|
||||||
self._current_operation,
|
|
||||||
self._target_temp,
|
|
||||||
None,
|
|
||||||
"HEATING",
|
|
||||||
)
|
|
||||||
|
|
||||||
self._overlay_mode = self._current_operation
|
self._overlay_mode = self._current_operation
|
||||||
|
|
||||||
@property
|
|
||||||
def is_aux_heat(self) -> Optional[bool]:
|
|
||||||
"""Return true if aux heater.
|
|
||||||
|
|
||||||
Requires SUPPORT_AUX_HEAT.
|
|
||||||
"""
|
|
||||||
return None
|
|
||||||
|
|
||||||
def turn_aux_heat_on(self) -> None:
|
|
||||||
"""Turn auxiliary heater on."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def turn_aux_heat_off(self) -> None:
|
|
||||||
"""Turn auxiliary heater off."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
|
||||||
def swing_mode(self) -> Optional[str]:
|
|
||||||
"""Return the swing setting.
|
|
||||||
|
|
||||||
Requires SUPPORT_SWING_MODE.
|
|
||||||
"""
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def swing_modes(self) -> Optional[List[str]]:
|
|
||||||
"""Return the list of available swing modes.
|
|
||||||
|
|
||||||
Requires SUPPORT_SWING_MODE.
|
|
||||||
"""
|
|
||||||
return None
|
|
||||||
|
|
||||||
def set_swing_mode(self, swing_mode: str) -> None:
|
|
||||||
"""Set new target swing operation."""
|
|
||||||
pass
|
|
||||||
|
|
18
homeassistant/components/tado/const.py
Normal file
18
homeassistant/components/tado/const.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
"""Constant values for the Tado component."""
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
CONF_FALLBACK = "fallback"
|
||||||
|
|
||||||
|
# Types
|
||||||
|
TYPE_AIR_CONDITIONING = "AIR_CONDITIONING"
|
||||||
|
TYPE_HEATING = "HEATING"
|
||||||
|
TYPE_HOT_WATER = "HOT_WATER"
|
||||||
|
|
||||||
|
# Base modes
|
||||||
|
CONST_MODE_SMART_SCHEDULE = "SMART_SCHEDULE" # Use the schedule
|
||||||
|
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
|
|
@ -1,130 +1,109 @@
|
||||||
"""Support for Tado sensors for each zone."""
|
"""Support for Tado sensors for each zone."""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.const import ATTR_ID, ATTR_NAME, TEMP_CELSIUS
|
from homeassistant.const import TEMP_CELSIUS
|
||||||
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
|
|
||||||
from . import DATA_TADO
|
from . import DOMAIN, SIGNAL_TADO_UPDATE_RECEIVED
|
||||||
|
from .const import TYPE_AIR_CONDITIONING, TYPE_HEATING, TYPE_HOT_WATER
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
ATTR_DATA_ID = "data_id"
|
ZONE_SENSORS = {
|
||||||
ATTR_DEVICE = "device"
|
TYPE_HEATING: [
|
||||||
ATTR_ZONE = "zone"
|
"temperature",
|
||||||
|
"humidity",
|
||||||
|
"power",
|
||||||
|
"link",
|
||||||
|
"heating",
|
||||||
|
"tado mode",
|
||||||
|
"overlay",
|
||||||
|
"early start",
|
||||||
|
],
|
||||||
|
TYPE_AIR_CONDITIONING: [
|
||||||
|
"temperature",
|
||||||
|
"humidity",
|
||||||
|
"power",
|
||||||
|
"link",
|
||||||
|
"ac",
|
||||||
|
"tado mode",
|
||||||
|
"overlay",
|
||||||
|
],
|
||||||
|
TYPE_HOT_WATER: ["power", "link", "tado mode", "overlay"],
|
||||||
|
}
|
||||||
|
|
||||||
CLIMATE_HEAT_SENSOR_TYPES = [
|
DEVICE_SENSORS = ["tado bridge status"]
|
||||||
"temperature",
|
|
||||||
"humidity",
|
|
||||||
"power",
|
|
||||||
"link",
|
|
||||||
"heating",
|
|
||||||
"tado mode",
|
|
||||||
"overlay",
|
|
||||||
"early start",
|
|
||||||
]
|
|
||||||
|
|
||||||
CLIMATE_COOL_SENSOR_TYPES = [
|
|
||||||
"temperature",
|
|
||||||
"humidity",
|
|
||||||
"power",
|
|
||||||
"link",
|
|
||||||
"ac",
|
|
||||||
"tado mode",
|
|
||||||
"overlay",
|
|
||||||
]
|
|
||||||
|
|
||||||
HOT_WATER_SENSOR_TYPES = ["power", "link", "tado mode", "overlay"]
|
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||||
"""Set up the sensor platform."""
|
"""Set up the sensor platform."""
|
||||||
tado = hass.data[DATA_TADO]
|
tado = hass.data[DOMAIN]
|
||||||
|
|
||||||
try:
|
# Create zone sensors
|
||||||
zones = tado.get_zones()
|
entities = []
|
||||||
except RuntimeError:
|
for zone in tado.zones:
|
||||||
_LOGGER.error("Unable to get zone info from mytado")
|
entities.extend(
|
||||||
return
|
[
|
||||||
|
create_zone_sensor(tado, zone["name"], zone["id"], variable)
|
||||||
sensor_items = []
|
for variable in ZONE_SENSORS.get(zone["type"])
|
||||||
for zone in zones:
|
]
|
||||||
if zone["type"] == "HEATING":
|
|
||||||
for variable in CLIMATE_HEAT_SENSOR_TYPES:
|
|
||||||
sensor_items.append(
|
|
||||||
create_zone_sensor(tado, zone, zone["name"], zone["id"], variable)
|
|
||||||
)
|
|
||||||
elif zone["type"] == "HOT_WATER":
|
|
||||||
for variable in HOT_WATER_SENSOR_TYPES:
|
|
||||||
sensor_items.append(
|
|
||||||
create_zone_sensor(tado, zone, zone["name"], zone["id"], variable)
|
|
||||||
)
|
|
||||||
elif zone["type"] == "AIR_CONDITIONING":
|
|
||||||
for variable in CLIMATE_COOL_SENSOR_TYPES:
|
|
||||||
sensor_items.append(
|
|
||||||
create_zone_sensor(tado, zone, zone["name"], zone["id"], variable)
|
|
||||||
)
|
|
||||||
|
|
||||||
me_data = tado.get_me()
|
|
||||||
sensor_items.append(
|
|
||||||
create_device_sensor(
|
|
||||||
tado,
|
|
||||||
me_data,
|
|
||||||
me_data["homes"][0]["name"],
|
|
||||||
me_data["homes"][0]["id"],
|
|
||||||
"tado bridge status",
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
if sensor_items:
|
# Create device sensors
|
||||||
add_entities(sensor_items, True)
|
for home in tado.devices:
|
||||||
|
entities.extend(
|
||||||
|
[
|
||||||
|
create_device_sensor(tado, home["name"], home["id"], variable)
|
||||||
|
for variable in DEVICE_SENSORS
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
add_entities(entities, True)
|
||||||
|
|
||||||
|
|
||||||
def create_zone_sensor(tado, zone, name, zone_id, variable):
|
def create_zone_sensor(tado, name, zone_id, variable):
|
||||||
"""Create a zone sensor."""
|
"""Create a zone sensor."""
|
||||||
data_id = f"zone {name} {zone_id}"
|
return TadoSensor(tado, name, "zone", zone_id, variable)
|
||||||
|
|
||||||
tado.add_sensor(
|
|
||||||
data_id,
|
|
||||||
{ATTR_ZONE: zone, ATTR_NAME: name, ATTR_ID: zone_id, ATTR_DATA_ID: data_id},
|
|
||||||
)
|
|
||||||
|
|
||||||
return TadoSensor(tado, name, zone_id, variable, data_id)
|
|
||||||
|
|
||||||
|
|
||||||
def create_device_sensor(tado, device, name, device_id, variable):
|
def create_device_sensor(tado, name, device_id, variable):
|
||||||
"""Create a device sensor."""
|
"""Create a device sensor."""
|
||||||
data_id = f"device {name} {device_id}"
|
return TadoSensor(tado, name, "device", device_id, variable)
|
||||||
|
|
||||||
tado.add_sensor(
|
|
||||||
data_id,
|
|
||||||
{
|
|
||||||
ATTR_DEVICE: device,
|
|
||||||
ATTR_NAME: name,
|
|
||||||
ATTR_ID: device_id,
|
|
||||||
ATTR_DATA_ID: data_id,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
return TadoSensor(tado, name, device_id, variable, data_id)
|
|
||||||
|
|
||||||
|
|
||||||
class TadoSensor(Entity):
|
class TadoSensor(Entity):
|
||||||
"""Representation of a tado Sensor."""
|
"""Representation of a tado Sensor."""
|
||||||
|
|
||||||
def __init__(self, store, zone_name, zone_id, zone_variable, data_id):
|
def __init__(self, tado, zone_name, sensor_type, zone_id, zone_variable):
|
||||||
"""Initialize of the Tado Sensor."""
|
"""Initialize of the Tado Sensor."""
|
||||||
self._store = store
|
self._tado = tado
|
||||||
|
|
||||||
self.zone_name = zone_name
|
self.zone_name = zone_name
|
||||||
self.zone_id = zone_id
|
self.zone_id = zone_id
|
||||||
self.zone_variable = zone_variable
|
self.zone_variable = zone_variable
|
||||||
|
self.sensor_type = sensor_type
|
||||||
|
|
||||||
self._unique_id = f"{zone_variable} {zone_id}"
|
self._unique_id = f"{zone_variable} {zone_id}"
|
||||||
self._data_id = data_id
|
|
||||||
|
|
||||||
self._state = None
|
self._state = None
|
||||||
self._state_attributes = None
|
self._state_attributes = None
|
||||||
|
|
||||||
|
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),
|
||||||
|
async_update_callback,
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_id(self):
|
def unique_id(self):
|
||||||
"""Return the unique id."""
|
"""Return the unique id."""
|
||||||
|
@ -165,14 +144,16 @@ class TadoSensor(Entity):
|
||||||
if self.zone_variable == "humidity":
|
if self.zone_variable == "humidity":
|
||||||
return "mdi:water-percent"
|
return "mdi:water-percent"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def should_poll(self) -> bool:
|
||||||
|
"""Do not poll."""
|
||||||
|
return False
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Update method called when should_poll is true."""
|
"""Handle update callbacks."""
|
||||||
self._store.update()
|
try:
|
||||||
|
data = self._tado.data[self.sensor_type][self.zone_id]
|
||||||
data = self._store.get_data(self._data_id)
|
except KeyError:
|
||||||
|
|
||||||
if data is None:
|
|
||||||
_LOGGER.debug("Received no data for zone %s", self.zone_name)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
unit = TEMP_CELSIUS
|
unit = TEMP_CELSIUS
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue