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.helpers.dispatcher import dispatcher_send
from homeassistant.util import Throttle from homeassistant.util import Throttle
from .const import CONF_FALLBACK from .const import CONF_FALLBACK, DATA
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -27,12 +27,15 @@ SCAN_INTERVAL = timedelta(seconds=15)
CONFIG_SCHEMA = vol.Schema( 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, vol.Required(CONF_USERNAME): cv.string,
} vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_FALLBACK, default=True): cv.boolean,
}
],
) )
}, },
extra=vol.ALLOW_EXTRA, extra=vol.ALLOW_EXTRA,
@ -41,45 +44,54 @@ CONFIG_SCHEMA = vol.Schema(
def setup(hass, config): def setup(hass, config):
"""Set up of the Tado component.""" """Set up of the Tado component."""
username = config[DOMAIN][CONF_USERNAME] acc_list = config[DOMAIN]
password = config[DOMAIN][CONF_PASSWORD]
tadoconnector = TadoConnector(hass, username, password) api_data_list = []
if not tadoconnector.setup():
return False
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 = TadoConnector(hass, username, password, fallback)
tadoconnector.update() 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 # Load components
for component in TADO_COMPONENTS: for component in TADO_COMPONENTS:
load_platform( load_platform(
hass, hass, component, DOMAIN, {}, config,
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 TadoConnector: class TadoConnector:
"""An object to store the Tado data.""" """An object to store the Tado data."""
def __init__(self, hass, username, password): def __init__(self, hass, username, password, fallback):
"""Initialize Tado Connector.""" """Initialize Tado Connector."""
self.hass = hass self.hass = hass
self._username = username self._username = username
self._password = password self._password = password
self._fallback = fallback
self.device_id = None
self.tado = None self.tado = None
self.zones = None self.zones = None
self.devices = None self.devices = None
@ -88,6 +100,11 @@ class TadoConnector:
"device": {}, "device": {},
} }
@property
def fallback(self):
"""Return fallback flag to Smart Schedule."""
return self._fallback
def setup(self): def setup(self):
"""Connect to Tado and fetch the zones.""" """Connect to Tado and fetch the zones."""
try: try:
@ -101,7 +118,7 @@ class TadoConnector:
# Load zones and devices # Load zones and devices
self.zones = self.tado.getZones() self.zones = self.tado.getZones()
self.devices = self.tado.getMe()["homes"] self.devices = self.tado.getMe()["homes"]
self.device_id = self.devices[0]["id"]
return True return True
@Throttle(MIN_TIME_BETWEEN_UPDATES) @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.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect 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 ( from .const import (
CONST_MODE_OFF, CONST_MODE_OFF,
CONST_MODE_SMART_SCHEDULE, CONST_MODE_SMART_SCHEDULE,
@ -70,21 +70,20 @@ 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[DOMAIN] api_list = hass.data[DOMAIN][DATA]
entities = [] entities = []
for zone in tado.zones:
entity = create_climate_entity( for tado in api_list:
tado, zone["name"], zone["id"], discovery_info[CONF_FALLBACK] for zone in tado.zones:
) entity = create_climate_entity(tado, zone["name"], zone["id"])
if entity: if entity:
entities.append(entity) entities.append(entity)
if entities: if entities:
add_entities(entities, True) 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.""" """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) _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) step = temperatures["celsius"].get("step", PRECISION_TENTHS)
entity = TadoClimate( entity = TadoClimate(
tado, tado, name, zone_id, zone_type, min_temp, max_temp, step, ac_support_heat,
name,
zone_id,
zone_type,
min_temp,
max_temp,
step,
ac_support_heat,
fallback,
) )
return entity return entity
@ -138,7 +129,6 @@ class TadoClimate(ClimateDevice):
max_temp, max_temp,
step, step,
ac_support_heat, ac_support_heat,
fallback,
): ):
"""Initialize of Tado climate entity.""" """Initialize of Tado climate entity."""
self._tado = tado self._tado = tado
@ -146,6 +136,7 @@ class TadoClimate(ClimateDevice):
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.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_device = zone_type == TYPE_AIR_CONDITIONING
self._ac_support_heat = ac_support_heat self._ac_support_heat = ac_support_heat
@ -162,7 +153,7 @@ class TadoClimate(ClimateDevice):
self._step = step self._step = step
self._target_temp = None self._target_temp = None
if fallback: if tado.fallback:
_LOGGER.debug("Default overlay is set to TADO MODE") _LOGGER.debug("Default overlay is set to TADO MODE")
# Fallback to Smart Schedule at next Schedule switch # Fallback to Smart Schedule at next Schedule switch
self._default_overlay = CONST_OVERLAY_TADO_MODE self._default_overlay = CONST_OVERLAY_TADO_MODE
@ -199,6 +190,11 @@ class TadoClimate(ClimateDevice):
"""Return the name of the entity.""" """Return the name of the entity."""
return self.zone_name return self.zone_name
@property
def unique_id(self):
"""Return the unique id."""
return self._unique_id
@property @property
def should_poll(self) -> bool: def should_poll(self) -> bool:
"""Do not poll.""" """Do not poll."""

View File

@ -2,6 +2,7 @@
# Configuration # Configuration
CONF_FALLBACK = "fallback" CONF_FALLBACK = "fallback"
DATA = "data"
# Types # Types
TYPE_AIR_CONDITIONING = "AIR_CONDITIONING" 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.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity 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 from .const import TYPE_AIR_CONDITIONING, TYPE_HEATING, TYPE_HOT_WATER
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -40,26 +40,29 @@ DEVICE_SENSORS = ["tado bridge status"]
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[DOMAIN] api_list = hass.data[DOMAIN][DATA]
# Create zone sensors
entities = [] 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 tado in api_list:
for home in tado.devices: # Create zone sensors
entities.extend(
[ for zone in tado.zones:
create_device_sensor(tado, home["name"], home["id"], variable) entities.extend(
for variable in DEVICE_SENSORS [
] 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) add_entities(entities, True)
@ -86,7 +89,7 @@ class TadoSensor(Entity):
self.zone_variable = zone_variable self.zone_variable = zone_variable
self.sensor_type = sensor_type 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 = None
self._state_attributes = None self._state_attributes = None
@ -227,23 +230,16 @@ class TadoSensor(Entity):
self._state = data["tadoMode"] self._state = data["tadoMode"]
elif self.zone_variable == "overlay": elif self.zone_variable == "overlay":
if "overlay" in data and data["overlay"] is not None: self._state = "overlay" in data and data["overlay"] is not None
self._state = True self._state_attributes = (
self._state_attributes = { {"termination": data["overlay"]["termination"]["type"]}
"termination": data["overlay"]["termination"]["type"] if self._state
} else {}
else: )
self._state = False
self._state_attributes = {}
elif self.zone_variable == "early start": elif self.zone_variable == "early start":
if "preparation" in data and data["preparation"] is not None: self._state = "preparation" in data and data["preparation"] is not None
self._state = True
else:
self._state = False
elif self.zone_variable == "open window": elif self.zone_variable == "open window":
if "openWindowDetected" in data: self._state = "openWindow" in data and data["openWindow"] is not None
self._state = data["openWindowDetected"] self._state_attributes = data["openWindow"] if self._state else {}
else:
self._state = False