mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 20:27:08 +00:00
Add support of multiple Tado accounts (#31527)
* Added support of multiple Tado accounts Changed geberation of sensor unique id (breaking change) * Fixed lints * Fixed error detecting opened window * Changed gereration of unique id of climate * Removed commented code and added comments
This commit is contained in:
parent
8a6158116a
commit
3df2cb6b78
@ -12,7 +12,7 @@ from homeassistant.helpers.discovery import load_platform
|
||||
from homeassistant.helpers.dispatcher import dispatcher_send
|
||||
from homeassistant.util import Throttle
|
||||
|
||||
from .const import CONF_FALLBACK
|
||||
from .const import CONF_FALLBACK, DATA
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -27,12 +27,15 @@ SCAN_INTERVAL = timedelta(seconds=15)
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
{
|
||||
DOMAIN: vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_USERNAME): cv.string,
|
||||
vol.Required(CONF_PASSWORD): cv.string,
|
||||
vol.Optional(CONF_FALLBACK, default=True): cv.boolean,
|
||||
}
|
||||
DOMAIN: vol.All(
|
||||
cv.ensure_list,
|
||||
[
|
||||
{
|
||||
vol.Required(CONF_USERNAME): cv.string,
|
||||
vol.Required(CONF_PASSWORD): cv.string,
|
||||
vol.Optional(CONF_FALLBACK, default=True): cv.boolean,
|
||||
}
|
||||
],
|
||||
)
|
||||
},
|
||||
extra=vol.ALLOW_EXTRA,
|
||||
@ -41,45 +44,54 @@ CONFIG_SCHEMA = vol.Schema(
|
||||
|
||||
def setup(hass, config):
|
||||
"""Set up of the Tado component."""
|
||||
username = config[DOMAIN][CONF_USERNAME]
|
||||
password = config[DOMAIN][CONF_PASSWORD]
|
||||
acc_list = config[DOMAIN]
|
||||
|
||||
tadoconnector = TadoConnector(hass, username, password)
|
||||
if not tadoconnector.setup():
|
||||
return False
|
||||
api_data_list = []
|
||||
|
||||
hass.data[DOMAIN] = tadoconnector
|
||||
for acc in acc_list:
|
||||
username = acc[CONF_USERNAME]
|
||||
password = acc[CONF_PASSWORD]
|
||||
fallback = acc[CONF_FALLBACK]
|
||||
|
||||
# Do first update
|
||||
tadoconnector.update()
|
||||
tadoconnector = TadoConnector(hass, username, password, fallback)
|
||||
if not tadoconnector.setup():
|
||||
continue
|
||||
|
||||
# Do first update
|
||||
tadoconnector.update()
|
||||
|
||||
api_data_list.append(tadoconnector)
|
||||
# Poll for updates in the background
|
||||
hass.helpers.event.track_time_interval(
|
||||
# we're using here tadoconnector as a parameter of lambda
|
||||
# to capture actual value instead of closuring of latest value
|
||||
lambda now, tc=tadoconnector: tc.update(),
|
||||
SCAN_INTERVAL,
|
||||
)
|
||||
|
||||
hass.data[DOMAIN] = {}
|
||||
hass.data[DOMAIN][DATA] = api_data_list
|
||||
|
||||
# Load components
|
||||
for component in TADO_COMPONENTS:
|
||||
load_platform(
|
||||
hass,
|
||||
component,
|
||||
DOMAIN,
|
||||
{CONF_FALLBACK: config[DOMAIN][CONF_FALLBACK]},
|
||||
config,
|
||||
hass, component, DOMAIN, {}, config,
|
||||
)
|
||||
|
||||
# Poll for updates in the background
|
||||
hass.helpers.event.track_time_interval(
|
||||
lambda now: tadoconnector.update(), SCAN_INTERVAL
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class TadoConnector:
|
||||
"""An object to store the Tado data."""
|
||||
|
||||
def __init__(self, hass, username, password):
|
||||
def __init__(self, hass, username, password, fallback):
|
||||
"""Initialize Tado Connector."""
|
||||
self.hass = hass
|
||||
self._username = username
|
||||
self._password = password
|
||||
self._fallback = fallback
|
||||
|
||||
self.device_id = None
|
||||
self.tado = None
|
||||
self.zones = None
|
||||
self.devices = None
|
||||
@ -88,6 +100,11 @@ class TadoConnector:
|
||||
"device": {},
|
||||
}
|
||||
|
||||
@property
|
||||
def fallback(self):
|
||||
"""Return fallback flag to Smart Schedule."""
|
||||
return self._fallback
|
||||
|
||||
def setup(self):
|
||||
"""Connect to Tado and fetch the zones."""
|
||||
try:
|
||||
@ -101,7 +118,7 @@ class TadoConnector:
|
||||
# Load zones and devices
|
||||
self.zones = self.tado.getZones()
|
||||
self.devices = self.tado.getMe()["homes"]
|
||||
|
||||
self.device_id = self.devices[0]["id"]
|
||||
return True
|
||||
|
||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||
|
@ -25,7 +25,7 @@ from homeassistant.const import ATTR_TEMPERATURE, PRECISION_TENTHS, TEMP_CELSIUS
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
|
||||
from . import CONF_FALLBACK, DOMAIN, SIGNAL_TADO_UPDATE_RECEIVED
|
||||
from . import DATA, DOMAIN, SIGNAL_TADO_UPDATE_RECEIVED
|
||||
from .const import (
|
||||
CONST_MODE_OFF,
|
||||
CONST_MODE_SMART_SCHEDULE,
|
||||
@ -70,21 +70,20 @@ SUPPORT_PRESET = [PRESET_AWAY, PRESET_HOME]
|
||||
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
"""Set up the Tado climate platform."""
|
||||
tado = hass.data[DOMAIN]
|
||||
|
||||
api_list = hass.data[DOMAIN][DATA]
|
||||
entities = []
|
||||
for zone in tado.zones:
|
||||
entity = create_climate_entity(
|
||||
tado, zone["name"], zone["id"], discovery_info[CONF_FALLBACK]
|
||||
)
|
||||
if entity:
|
||||
entities.append(entity)
|
||||
|
||||
for tado in api_list:
|
||||
for zone in tado.zones:
|
||||
entity = create_climate_entity(tado, zone["name"], zone["id"])
|
||||
if entity:
|
||||
entities.append(entity)
|
||||
|
||||
if entities:
|
||||
add_entities(entities, True)
|
||||
|
||||
|
||||
def create_climate_entity(tado, name: str, zone_id: int, fallback: bool):
|
||||
def create_climate_entity(tado, name: str, zone_id: int):
|
||||
"""Create a Tado climate entity."""
|
||||
capabilities = tado.get_capabilities(zone_id)
|
||||
_LOGGER.debug("Capabilities for zone %s: %s", zone_id, capabilities)
|
||||
@ -112,15 +111,7 @@ def create_climate_entity(tado, name: str, zone_id: int, fallback: bool):
|
||||
step = temperatures["celsius"].get("step", PRECISION_TENTHS)
|
||||
|
||||
entity = TadoClimate(
|
||||
tado,
|
||||
name,
|
||||
zone_id,
|
||||
zone_type,
|
||||
min_temp,
|
||||
max_temp,
|
||||
step,
|
||||
ac_support_heat,
|
||||
fallback,
|
||||
tado, name, zone_id, zone_type, min_temp, max_temp, step, ac_support_heat,
|
||||
)
|
||||
return entity
|
||||
|
||||
@ -138,7 +129,6 @@ class TadoClimate(ClimateDevice):
|
||||
max_temp,
|
||||
step,
|
||||
ac_support_heat,
|
||||
fallback,
|
||||
):
|
||||
"""Initialize of Tado climate entity."""
|
||||
self._tado = tado
|
||||
@ -146,6 +136,7 @@ class TadoClimate(ClimateDevice):
|
||||
self.zone_name = zone_name
|
||||
self.zone_id = zone_id
|
||||
self.zone_type = zone_type
|
||||
self._unique_id = f"{zone_type} {zone_id} {tado.device_id}"
|
||||
|
||||
self._ac_device = zone_type == TYPE_AIR_CONDITIONING
|
||||
self._ac_support_heat = ac_support_heat
|
||||
@ -162,7 +153,7 @@ class TadoClimate(ClimateDevice):
|
||||
self._step = step
|
||||
self._target_temp = None
|
||||
|
||||
if fallback:
|
||||
if tado.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
|
||||
@ -199,6 +190,11 @@ class TadoClimate(ClimateDevice):
|
||||
"""Return the name of the entity."""
|
||||
return self.zone_name
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return the unique id."""
|
||||
return self._unique_id
|
||||
|
||||
@property
|
||||
def should_poll(self) -> bool:
|
||||
"""Do not poll."""
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
# Configuration
|
||||
CONF_FALLBACK = "fallback"
|
||||
DATA = "data"
|
||||
|
||||
# Types
|
||||
TYPE_AIR_CONDITIONING = "AIR_CONDITIONING"
|
||||
|
@ -6,7 +6,7 @@ from homeassistant.core import callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
from . import DOMAIN, SIGNAL_TADO_UPDATE_RECEIVED
|
||||
from . import DATA, DOMAIN, SIGNAL_TADO_UPDATE_RECEIVED
|
||||
from .const import TYPE_AIR_CONDITIONING, TYPE_HEATING, TYPE_HOT_WATER
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@ -40,26 +40,29 @@ DEVICE_SENSORS = ["tado bridge status"]
|
||||
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
"""Set up the sensor platform."""
|
||||
tado = hass.data[DOMAIN]
|
||||
api_list = hass.data[DOMAIN][DATA]
|
||||
|
||||
# Create zone sensors
|
||||
entities = []
|
||||
for zone in tado.zones:
|
||||
entities.extend(
|
||||
[
|
||||
create_zone_sensor(tado, zone["name"], zone["id"], variable)
|
||||
for variable in ZONE_SENSORS.get(zone["type"])
|
||||
]
|
||||
)
|
||||
|
||||
# Create device sensors
|
||||
for home in tado.devices:
|
||||
entities.extend(
|
||||
[
|
||||
create_device_sensor(tado, home["name"], home["id"], variable)
|
||||
for variable in DEVICE_SENSORS
|
||||
]
|
||||
)
|
||||
for tado in api_list:
|
||||
# Create zone sensors
|
||||
|
||||
for zone in tado.zones:
|
||||
entities.extend(
|
||||
[
|
||||
create_zone_sensor(tado, zone["name"], zone["id"], variable)
|
||||
for variable in ZONE_SENSORS.get(zone["type"])
|
||||
]
|
||||
)
|
||||
|
||||
# Create device sensors
|
||||
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)
|
||||
|
||||
@ -86,7 +89,7 @@ class TadoSensor(Entity):
|
||||
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} {tado.device_id}"
|
||||
|
||||
self._state = None
|
||||
self._state_attributes = None
|
||||
@ -227,23 +230,16 @@ class TadoSensor(Entity):
|
||||
self._state = data["tadoMode"]
|
||||
|
||||
elif self.zone_variable == "overlay":
|
||||
if "overlay" in data and data["overlay"] is not None:
|
||||
self._state = True
|
||||
self._state_attributes = {
|
||||
"termination": data["overlay"]["termination"]["type"]
|
||||
}
|
||||
else:
|
||||
self._state = False
|
||||
self._state_attributes = {}
|
||||
self._state = "overlay" in data and data["overlay"] is not None
|
||||
self._state_attributes = (
|
||||
{"termination": data["overlay"]["termination"]["type"]}
|
||||
if self._state
|
||||
else {}
|
||||
)
|
||||
|
||||
elif self.zone_variable == "early start":
|
||||
if "preparation" in data and data["preparation"] is not None:
|
||||
self._state = True
|
||||
else:
|
||||
self._state = False
|
||||
self._state = "preparation" in data and data["preparation"] is not None
|
||||
|
||||
elif self.zone_variable == "open window":
|
||||
if "openWindowDetected" in data:
|
||||
self._state = data["openWindowDetected"]
|
||||
else:
|
||||
self._state = False
|
||||
self._state = "openWindow" in data and data["openWindow"] is not None
|
||||
self._state_attributes = data["openWindow"] if self._state else {}
|
||||
|
Loading…
x
Reference in New Issue
Block a user