From 783cc1eacd34cae0259fcaef16c9e6a0ea371cad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20=C3=85str=C3=B6m?= Date: Wed, 22 Sep 2021 14:01:30 +0200 Subject: [PATCH] Optimise requests to the tado servers (#56261) This avoids calling the tado servers unnecessarily many times, especially for bigger homes. This is done by calling aggregating endpoints instead of iterating over the zones and devices and calling endpoints over and over. --- homeassistant/components/tado/__init__.py | 103 +++++-- homeassistant/components/tado/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/tado/util.py | 5 + tests/fixtures/tado/zone_states.json | 292 ++++++++++++++++++++ 6 files changed, 374 insertions(+), 32 deletions(-) create mode 100644 tests/fixtures/tado/zone_states.json diff --git a/homeassistant/components/tado/__init__.py b/homeassistant/components/tado/__init__.py index 3cb2abe90f6..7a1965e31fb 100644 --- a/homeassistant/components/tado/__init__.py +++ b/homeassistant/components/tado/__init__.py @@ -3,6 +3,7 @@ from datetime import timedelta import logging from PyTado.interface import Tado +from PyTado.zone import TadoZone from requests import RequestException import requests.exceptions @@ -155,52 +156,96 @@ class TadoConnector: @Throttle(MIN_TIME_BETWEEN_UPDATES) def update(self): """Update the registered zones.""" - for device in self.devices: - self.update_sensor("device", device["shortSerialNo"]) - for zone in self.zones: - self.update_sensor("zone", zone["id"]) + self.update_devices() + self.update_zones() self.data["weather"] = self.tado.getWeather() dispatcher_send( self.hass, SIGNAL_TADO_UPDATE_RECEIVED.format(self.home_id, "weather", "data"), ) - def update_sensor(self, sensor_type, sensor): - """Update the internal data from Tado.""" - _LOGGER.debug("Updating %s %s", sensor_type, sensor) - try: - if sensor_type == "device": - data = self.tado.getDeviceInfo(sensor) + def update_devices(self): + """Update the device data from Tado.""" + devices = self.tado.getDevices() + for device in devices: + device_short_serial_no = device["shortSerialNo"] + _LOGGER.debug("Updating device %s", device_short_serial_no) + try: if ( INSIDE_TEMPERATURE_MEASUREMENT - in data["characteristics"]["capabilities"] + in device["characteristics"]["capabilities"] ): - data[TEMP_OFFSET] = self.tado.getDeviceInfo(sensor, TEMP_OFFSET) - elif sensor_type == "zone": - data = self.tado.getZoneState(sensor) - else: - _LOGGER.debug("Unknown sensor: %s", sensor_type) + device[TEMP_OFFSET] = self.tado.getDeviceInfo( + device_short_serial_no, TEMP_OFFSET + ) + except RuntimeError: + _LOGGER.error( + "Unable to connect to Tado while updating device %s", + device_short_serial_no, + ) return - except RuntimeError: - _LOGGER.error( - "Unable to connect to Tado while updating %s %s", - sensor_type, - sensor, + + self.data["device"][device_short_serial_no] = device + + _LOGGER.debug( + "Dispatching update to %s device %s: %s", + self.home_id, + device_short_serial_no, + device, ) + dispatcher_send( + self.hass, + SIGNAL_TADO_UPDATE_RECEIVED.format( + self.home_id, "device", device_short_serial_no + ), + ) + + def update_zones(self): + """Update the zone data from Tado.""" + try: + zone_states = self.tado.getZoneStates()["zoneStates"] + except RuntimeError: + _LOGGER.error("Unable to connect to Tado while updating zones") return - self.data[sensor_type][sensor] = data + for zone in self.zones: + zone_id = zone["id"] + _LOGGER.debug("Updating zone %s", zone_id) + zone_state = TadoZone(zone_states[str(zone_id)], zone_id) + + self.data["zone"][zone_id] = zone_state + + _LOGGER.debug( + "Dispatching update to %s zone %s: %s", + self.home_id, + zone_id, + zone_state, + ) + dispatcher_send( + self.hass, + SIGNAL_TADO_UPDATE_RECEIVED.format(self.home_id, "zone", zone["id"]), + ) + + def update_zone(self, zone_id): + """Update the internal data from Tado.""" + _LOGGER.debug("Updating zone %s", zone_id) + try: + data = self.tado.getZoneState(zone_id) + except RuntimeError: + _LOGGER.error("Unable to connect to Tado while updating zone %s", zone_id) + return + + self.data["zone"][zone_id] = data _LOGGER.debug( - "Dispatching update to %s %s %s: %s", + "Dispatching update to %s zone %s: %s", self.home_id, - sensor_type, - sensor, + zone_id, data, ) dispatcher_send( self.hass, - SIGNAL_TADO_UPDATE_RECEIVED.format(self.home_id, sensor_type, sensor), + SIGNAL_TADO_UPDATE_RECEIVED.format(self.home_id, "zone", zone_id), ) def get_capabilities(self, zone_id): @@ -210,7 +255,7 @@ class TadoConnector: def reset_zone_overlay(self, zone_id): """Reset the zone back to the default operation.""" self.tado.resetZoneOverlay(zone_id) - self.update_sensor("zone", zone_id) + self.update_zone(zone_id) def set_presence( self, @@ -262,7 +307,7 @@ class TadoConnector: except RequestException as exc: _LOGGER.error("Could not set zone overlay: %s", exc) - self.update_sensor("zone", zone_id) + self.update_zone(zone_id) def set_zone_off(self, zone_id, overlay_mode, device_type="HEATING"): """Set a zone to off.""" @@ -273,7 +318,7 @@ class TadoConnector: except RequestException as exc: _LOGGER.error("Could not set zone overlay: %s", exc) - self.update_sensor("zone", zone_id) + self.update_zone(zone_id) def set_temperature_offset(self, device_id, offset): """Set temperature offset of device.""" diff --git a/homeassistant/components/tado/manifest.json b/homeassistant/components/tado/manifest.json index 8cf0ed260e8..a77974ab803 100644 --- a/homeassistant/components/tado/manifest.json +++ b/homeassistant/components/tado/manifest.json @@ -2,7 +2,7 @@ "domain": "tado", "name": "Tado", "documentation": "https://www.home-assistant.io/integrations/tado", - "requirements": ["python-tado==0.10.0"], + "requirements": ["python-tado==0.12.0"], "codeowners": ["@michaelarnauts", "@noltari"], "config_flow": true, "homekit": { diff --git a/requirements_all.txt b/requirements_all.txt index 986b79e278d..5b16bd0f064 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1926,7 +1926,7 @@ python-sochain-api==0.0.2 python-songpal==0.12 # homeassistant.components.tado -python-tado==0.10.0 +python-tado==0.12.0 # homeassistant.components.telegram_bot python-telegram-bot==13.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c0dff9c66d4..f9428de92d3 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1107,7 +1107,7 @@ python-smarttub==0.0.25 python-songpal==0.12 # homeassistant.components.tado -python-tado==0.10.0 +python-tado==0.12.0 # homeassistant.components.twitch python-twitch-client==0.6.0 diff --git a/tests/components/tado/util.py b/tests/components/tado/util.py index ce1dd92942d..a5e23b6021a 100644 --- a/tests/components/tado/util.py +++ b/tests/components/tado/util.py @@ -20,6 +20,7 @@ async def async_init_integration( me_fixture = "tado/me.json" weather_fixture = "tado/weather.json" zones_fixture = "tado/zones.json" + zone_states_fixture = "tado/zone_states.json" # WR1 Device device_wr1_fixture = "tado/device_wr1.json" @@ -80,6 +81,10 @@ async def async_init_integration( "https://my.tado.com/api/v2/homes/1/zones", text=load_fixture(zones_fixture), ) + m.get( + "https://my.tado.com/api/v2/homes/1/zoneStates", + text=load_fixture(zone_states_fixture), + ) m.get( "https://my.tado.com/api/v2/homes/1/zones/5/capabilities", text=load_fixture(zone_5_capabilities_fixture), diff --git a/tests/fixtures/tado/zone_states.json b/tests/fixtures/tado/zone_states.json new file mode 100644 index 00000000000..c5bd0dfbe2c --- /dev/null +++ b/tests/fixtures/tado/zone_states.json @@ -0,0 +1,292 @@ +{ + "zoneStates": { + "1": { + "tadoMode": "HOME", + "geolocationOverride": false, + "geolocationOverrideDisableTime": null, + "preparation": null, + "setting": { + "type": "HEATING", + "power": "ON", + "temperature": { + "celsius": 20.50, + "fahrenheit": 68.90 + } + }, + "overlayType": "MANUAL", + "overlay": { + "type": "MANUAL", + "setting": { + "type": "HEATING", + "power": "ON", + "temperature": { + "celsius": 20.50, + "fahrenheit": 68.90 + } + }, + "termination": { + "type": "MANUAL", + "typeSkillBasedApp": "MANUAL", + "projectedExpiry": null + } + }, + "openWindow": null, + "nextScheduleChange": { + "start": "2020-03-10T17:00:00Z", + "setting": { + "type": "HEATING", + "power": "ON", + "temperature": { + "celsius": 21.00, + "fahrenheit": 69.80 + } + } + }, + "nextTimeBlock": { + "start": "2020-03-10T17:00:00.000Z" + }, + "link": { + "state": "ONLINE" + }, + "activityDataPoints": { + "heatingPower": { + "type": "PERCENTAGE", + "percentage": 0.00, + "timestamp": "2020-03-10T07:47:45.978Z" + } + }, + "sensorDataPoints": { + "insideTemperature": { + "celsius": 20.65, + "fahrenheit": 69.17, + "timestamp": "2020-03-10T07:44:11.947Z", + "type": "TEMPERATURE", + "precision": { + "celsius": 0.1, + "fahrenheit": 0.1 + } + }, + "humidity": { + "type": "PERCENTAGE", + "percentage": 45.20, + "timestamp": "2020-03-10T07:44:11.947Z" + } + } + }, + "2": { + "tadoMode": "HOME", + "geolocationOverride": false, + "geolocationOverrideDisableTime": null, + "preparation": null, + "setting": { + "type": "HOT_WATER", + "power": "ON", + "temperature": { + "celsius": 65.00, + "fahrenheit": 149.00 + } + }, + "overlayType": null, + "overlay": null, + "openWindow": null, + "nextScheduleChange": { + "start": "2020-03-10T22:00:00Z", + "setting": { + "type": "HOT_WATER", + "power": "OFF", + "temperature": null + } + }, + "nextTimeBlock": { + "start": "2020-03-10T22:00:00.000Z" + }, + "link": { + "state": "ONLINE" + }, + "activityDataPoints": {}, + "sensorDataPoints": {} + }, + "3": { + "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 + } + } + }, + "4": { + "activityDataPoints": {}, + "preparation": null, + "openWindow": null, + "tadoMode": "HOME", + "nextScheduleChange": { + "setting": { + "temperature": { + "fahrenheit": 149, + "celsius": 65 + }, + "type": "HOT_WATER", + "power": "ON" + }, + "start": "2020-03-26T05:00:00Z" + }, + "nextTimeBlock": { + "start": "2020-03-26T05:00:00.000Z" + }, + "overlay": { + "setting": { + "temperature": { + "celsius": 30, + "fahrenheit": 86 + }, + "type": "HOT_WATER", + "power": "ON" + }, + "termination": { + "type": "TADO_MODE", + "projectedExpiry": "2020-03-26T05:00:00Z", + "typeSkillBasedApp": "TADO_MODE" + }, + "type": "MANUAL" + }, + "geolocationOverride": false, + "geolocationOverrideDisableTime": null, + "sensorDataPoints": {}, + "overlayType": "MANUAL", + "link": { + "state": "ONLINE" + }, + "setting": { + "type": "HOT_WATER", + "temperature": { + "fahrenheit": 86, + "celsius": 30 + }, + "power": "ON" + } + }, + "5": { + "tadoMode": "HOME", + "geolocationOverride": false, + "geolocationOverrideDisableTime": null, + "preparation": null, + "setting": { + "type": "AIR_CONDITIONING", + "power": "ON", + "mode": "HEAT", + "temperature": { + "celsius": 20.00, + "fahrenheit": 68.00 + }, + "fanSpeed": "AUTO", + "swing": "ON" + }, + "overlayType": null, + "overlay": null, + "openWindow": null, + "nextScheduleChange": { + "start": "2020-03-28T04:30:00Z", + "setting": { + "type": "AIR_CONDITIONING", + "power": "ON", + "mode": "HEAT", + "temperature": { + "celsius": 23.00, + "fahrenheit": 73.40 + }, + "fanSpeed": "AUTO", + "swing": "ON" + } + }, + "nextTimeBlock": { + "start": "2020-03-28T04:30:00.000Z" + }, + "link": { + "state": "ONLINE" + }, + "activityDataPoints": { + "acPower": { + "timestamp": "2020-03-27T23:02:22.260Z", + "type": "POWER", + "value": "ON" + } + }, + "sensorDataPoints": { + "insideTemperature": { + "celsius": 20.88, + "fahrenheit": 69.58, + "timestamp": "2020-03-28T02:09:27.830Z", + "type": "TEMPERATURE", + "precision": { + "celsius": 0.1, + "fahrenheit": 0.1 + } + }, + "humidity": { + "type": "PERCENTAGE", + "percentage": 42.30, + "timestamp": "2020-03-28T02:09:27.830Z" + } + } + } + } +} \ No newline at end of file