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:
Victor Vostrikov 2020-02-11 17:46:02 +01:00 committed by GitHub
parent 8a6158116a
commit 3df2cb6b78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 93 additions and 83 deletions

View File

@ -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(
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,31 +44,38 @@ 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)
api_data_list = []
for acc in acc_list:
username = acc[CONF_USERNAME]
password = acc[CONF_PASSWORD]
fallback = acc[CONF_FALLBACK]
tadoconnector = TadoConnector(hass, username, password, fallback)
if not tadoconnector.setup():
return False
hass.data[DOMAIN] = tadoconnector
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,
)
# Poll for updates in the background
hass.helpers.event.track_time_interval(
lambda now: tadoconnector.update(), SCAN_INTERVAL
hass, component, DOMAIN, {}, config,
)
return True
@ -74,12 +84,14 @@ def setup(hass, config):
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)

View File

@ -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,13 +70,12 @@ 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 tado in api_list:
for zone in tado.zones:
entity = create_climate_entity(
tado, zone["name"], zone["id"], discovery_info[CONF_FALLBACK]
)
entity = create_climate_entity(tado, zone["name"], zone["id"])
if entity:
entities.append(entity)
@ -84,7 +83,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
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."""

View File

@ -2,6 +2,7 @@
# Configuration
CONF_FALLBACK = "fallback"
DATA = "data"
# Types
TYPE_AIR_CONDITIONING = "AIR_CONDITIONING"

View File

@ -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,10 +40,13 @@ 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 tado in api_list:
# Create zone sensors
for zone in tado.zones:
entities.extend(
[
@ -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 {}