From 3566803d2ef17cd5883971ba1ab70c1885833f43 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 31 Mar 2020 17:29:45 -0500 Subject: [PATCH] Fix setting zone overlays for tados that support swing (#33439) * Fix setting zone overlays for tados that support swing * Support for changing swing mode will come at a later time as another upstream update is required. * remove debug * style --- homeassistant/components/tado/__init__.py | 7 +- homeassistant/components/tado/climate.py | 46 +++++++++++-- homeassistant/components/tado/const.py | 4 ++ homeassistant/components/tado/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/tado/test_climate.py | 29 +++++++++ tests/components/tado/util.py | 14 ++++ tests/fixtures/tado/smartac3.with_swing.json | 64 +++++++++++++++++++ .../tado/zone_with_swing_capabilities.json | 46 +++++++++++++ tests/fixtures/tado/zones.json | 48 ++++++++++++++ 11 files changed, 253 insertions(+), 11 deletions(-) create mode 100644 tests/fixtures/tado/smartac3.with_swing.json create mode 100644 tests/fixtures/tado/zone_with_swing_capabilities.json diff --git a/homeassistant/components/tado/__init__.py b/homeassistant/components/tado/__init__.py index 46dba04a77e..1dba5f5f29e 100644 --- a/homeassistant/components/tado/__init__.py +++ b/homeassistant/components/tado/__init__.py @@ -186,10 +186,11 @@ class TadoConnector: device_type="HEATING", mode=None, fan_speed=None, + swing=None, ): """Set a zone overlay.""" _LOGGER.debug( - "Set overlay for zone %s: overlay_mode=%s, temp=%s, duration=%s, type=%s, mode=%s fan_speed=%s", + "Set overlay for zone %s: overlay_mode=%s, temp=%s, duration=%s, type=%s, mode=%s fan_speed=%s swing=%s", zone_id, overlay_mode, temperature, @@ -197,6 +198,7 @@ class TadoConnector: device_type, mode, fan_speed, + swing, ) try: @@ -208,7 +210,8 @@ class TadoConnector: device_type, "ON", mode, - fan_speed, + fanSpeed=fan_speed, + swing=swing, ) except RequestException as exc: diff --git a/homeassistant/components/tado/climate.py b/homeassistant/components/tado/climate.py index 224960ea3eb..2c6e49f3273 100644 --- a/homeassistant/components/tado/climate.py +++ b/homeassistant/components/tado/climate.py @@ -11,6 +11,7 @@ from homeassistant.components.climate.const import ( PRESET_HOME, SUPPORT_FAN_MODE, SUPPORT_PRESET_MODE, + SUPPORT_SWING_MODE, SUPPORT_TARGET_TEMPERATURE, ) from homeassistant.const import ATTR_TEMPERATURE, PRECISION_TENTHS, TEMP_CELSIUS @@ -35,6 +36,7 @@ from .const import ( SUPPORT_PRESET, TADO_HVAC_ACTION_TO_HA_HVAC_ACTION, TADO_MODES_WITH_NO_TEMP_SETTING, + TADO_SWING_OFF, TADO_TO_HA_FAN_MODE_MAP, TADO_TO_HA_HVAC_MODE_MAP, TYPE_AIR_CONDITIONING, @@ -85,6 +87,9 @@ def create_climate_entity(tado, name: str, zone_id: int): continue supported_hvac_modes.append(TADO_TO_HA_HVAC_MODE_MAP[mode]) + if capabilities[mode].get("swings"): + support_flags |= SUPPORT_SWING_MODE + if not capabilities[mode].get("fanSpeeds"): continue @@ -197,6 +202,7 @@ class TadoClimate(ClimateDevice): self._current_tado_fan_speed = CONST_FAN_OFF self._current_tado_hvac_mode = CONST_MODE_OFF self._current_tado_hvac_action = CURRENT_HVAC_OFF + self._current_tado_swing_mode = TADO_SWING_OFF self._undo_dispatcher = None self._tado_zone_data = None @@ -378,6 +384,25 @@ class TadoClimate(ClimateDevice): return self._heat_max_temp + @property + def swing_mode(self): + """Active swing mode for the device.""" + return self._current_tado_swing_mode + + @property + def swing_modes(self): + """Swing modes for the device.""" + if self._support_flags & SUPPORT_SWING_MODE: + # Currently we only support off. + # On will be added in the future in an update + # to PyTado + return [TADO_SWING_OFF] + return None + + def set_swing_mode(self, swing_mode): + """Set swing modes for the device.""" + self._control_hvac(swing_mode=swing_mode) + @callback def _async_update_zone_data(self): """Load tado data into zone.""" @@ -408,7 +433,9 @@ class TadoClimate(ClimateDevice): elif self._target_temp < self._heat_min_temp: self._target_temp = self._heat_min_temp - def _control_hvac(self, hvac_mode=None, target_temp=None, fan_mode=None): + def _control_hvac( + self, hvac_mode=None, target_temp=None, fan_mode=None, swing_mode=None + ): """Send new target temperature to Tado.""" if hvac_mode: @@ -420,6 +447,9 @@ class TadoClimate(ClimateDevice): if fan_mode: self._current_tado_fan_speed = fan_mode + if swing_mode: + self._current_tado_swing_mode = swing_mode + self._normalize_target_temp_for_hvac_mode() # tado does not permit setting the fan speed to @@ -464,6 +494,13 @@ class TadoClimate(ClimateDevice): # A temperature cannot be passed with these modes temperature_to_send = None + fan_speed = None + if self._support_flags & SUPPORT_FAN_MODE: + fan_speed = self._current_tado_fan_speed + swing = None + if self._support_flags & SUPPORT_SWING_MODE: + swing = self._current_tado_swing_mode + self._tado.set_zone_overlay( zone_id=self.zone_id, overlay_mode=overlay_mode, # What to do when the period ends @@ -471,9 +508,6 @@ class TadoClimate(ClimateDevice): duration=None, device_type=self.zone_type, mode=self._current_tado_hvac_mode, - fan_speed=( - self._current_tado_fan_speed - if (self._support_flags & SUPPORT_FAN_MODE) - else None - ), # api defaults to not sending fanSpeed if not specified + fan_speed=fan_speed, # api defaults to not sending fanSpeed if None specified + swing=swing, # api defaults to not sending swing if None specified ) diff --git a/homeassistant/components/tado/const.py b/homeassistant/components/tado/const.py index 542437d0af0..ab965de035a 100644 --- a/homeassistant/components/tado/const.py +++ b/homeassistant/components/tado/const.py @@ -131,3 +131,7 @@ TADO_TO_HA_FAN_MODE_MAP = {value: key for key, value in HA_TO_TADO_FAN_MODE_MAP. DEFAULT_TADO_PRECISION = 0.1 SUPPORT_PRESET = [PRESET_AWAY, PRESET_HOME] + + +TADO_SWING_OFF = "OFF" +TADO_SWING_ON = "ON" diff --git a/homeassistant/components/tado/manifest.json b/homeassistant/components/tado/manifest.json index e84072b5985..ce4679a23e2 100644 --- a/homeassistant/components/tado/manifest.json +++ b/homeassistant/components/tado/manifest.json @@ -3,7 +3,7 @@ "name": "Tado", "documentation": "https://www.home-assistant.io/integrations/tado", "requirements": [ - "python-tado==0.5.0" + "python-tado==0.6.0" ], "dependencies": [], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index 08f54a3924a..04e3b1c99a9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1668,7 +1668,7 @@ python-songpal==0.11.2 python-synology==0.4.0 # homeassistant.components.tado -python-tado==0.5.0 +python-tado==0.6.0 # homeassistant.components.telegram_bot python-telegram-bot==11.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0df53b2e0b6..2ea01f58880 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -620,7 +620,7 @@ python-miio==0.4.8 python-nest==4.1.0 # homeassistant.components.tado -python-tado==0.5.0 +python-tado==0.6.0 # homeassistant.components.twitch python-twitch-client==0.6.0 diff --git a/tests/components/tado/test_climate.py b/tests/components/tado/test_climate.py index 602f4d8424f..dfb2973f4cb 100644 --- a/tests/components/tado/test_climate.py +++ b/tests/components/tado/test_climate.py @@ -57,3 +57,32 @@ async def test_heater(hass): # Only test for a subset of attributes in case # HA changes the implementation and a new one appears assert all(item in state.attributes.items() for item in expected_attributes.items()) + + +async def test_smartac_with_swing(hass): + """Test creation of smart ac with swing climate.""" + + await async_init_integration(hass) + + state = hass.states.get("climate.air_conditioning_with_swing") + assert state.state == "auto" + + expected_attributes = { + "current_humidity": 42.3, + "current_temperature": 20.9, + "fan_mode": "auto", + "fan_modes": ["auto", "high", "medium", "low"], + "friendly_name": "Air Conditioning with swing", + "hvac_action": "heating", + "hvac_modes": ["off", "auto", "heat", "cool", "heat_cool", "dry", "fan_only"], + "max_temp": 30.0, + "min_temp": 16.0, + "preset_mode": "home", + "preset_modes": ["away", "home"], + "supported_features": 57, + "target_temp_step": 1.0, + "temperature": 20.0, + } + # Only test for a subset of attributes in case + # HA changes the implementation and a new one appears + assert all(item in state.attributes.items() for item in expected_attributes.items()) diff --git a/tests/components/tado/util.py b/tests/components/tado/util.py index 7ee4c17058d..1b7e1ad888e 100644 --- a/tests/components/tado/util.py +++ b/tests/components/tado/util.py @@ -19,6 +19,11 @@ async def async_init_integration( devices_fixture = "tado/devices.json" me_fixture = "tado/me.json" zones_fixture = "tado/zones.json" + + # Smart AC with Swing + zone_5_state_fixture = "tado/smartac3.with_swing.json" + zone_5_capabilities_fixture = "tado/zone_with_swing_capabilities.json" + # Water Heater 2 zone_4_state_fixture = "tado/tadov2.water_heater.heating.json" zone_4_capabilities_fixture = "tado/water_heater_zone_capabilities.json" @@ -31,6 +36,7 @@ async def async_init_integration( zone_2_state_fixture = "tado/tadov2.water_heater.auto_mode.json" zone_2_capabilities_fixture = "tado/water_heater_zone_capabilities.json" + # Tado V2 with manual heating zone_1_state_fixture = "tado/tadov2.heating.manual_mode.json" zone_1_capabilities_fixture = "tado/tadov2.zone_capabilities.json" @@ -47,6 +53,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/zones/5/capabilities", + text=load_fixture(zone_5_capabilities_fixture), + ) m.get( "https://my.tado.com/api/v2/homes/1/zones/4/capabilities", text=load_fixture(zone_4_capabilities_fixture), @@ -63,6 +73,10 @@ async def async_init_integration( "https://my.tado.com/api/v2/homes/1/zones/1/capabilities", text=load_fixture(zone_1_capabilities_fixture), ) + m.get( + "https://my.tado.com/api/v2/homes/1/zones/5/state", + text=load_fixture(zone_5_state_fixture), + ) m.get( "https://my.tado.com/api/v2/homes/1/zones/4/state", text=load_fixture(zone_4_state_fixture), diff --git a/tests/fixtures/tado/smartac3.with_swing.json b/tests/fixtures/tado/smartac3.with_swing.json new file mode 100644 index 00000000000..c72cc2ad50b --- /dev/null +++ b/tests/fixtures/tado/smartac3.with_swing.json @@ -0,0 +1,64 @@ +{ + "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" + } + } +} diff --git a/tests/fixtures/tado/zone_with_swing_capabilities.json b/tests/fixtures/tado/zone_with_swing_capabilities.json new file mode 100644 index 00000000000..fc954890e2a --- /dev/null +++ b/tests/fixtures/tado/zone_with_swing_capabilities.json @@ -0,0 +1,46 @@ +{ + "type": "AIR_CONDITIONING", + "AUTO": { + "fanSpeeds": ["AUTO", "HIGH", "MIDDLE", "LOW"], + "swings": ["OFF", "ON"] + }, + "COOL": { + "temperatures": { + "celsius": { + "min": 18, + "max": 30, + "step": 1.0 + }, + "fahrenheit": { + "min": 64, + "max": 86, + "step": 1.0 + } + }, + "fanSpeeds": ["AUTO", "HIGH", "MIDDLE", "LOW"], + "swings": ["OFF", "ON"] + }, + "DRY": { + "swings": ["OFF", "ON"] + }, + "FAN": { + "fanSpeeds": ["AUTO", "HIGH", "MIDDLE", "LOW"], + "swings": ["OFF", "ON"] + }, + "HEAT": { + "temperatures": { + "celsius": { + "min": 16, + "max": 30, + "step": 1.0 + }, + "fahrenheit": { + "min": 61, + "max": 86, + "step": 1.0 + } + }, + "fanSpeeds": ["AUTO", "HIGH", "MIDDLE", "LOW"], + "swings": ["OFF", "ON"] + } +} diff --git a/tests/fixtures/tado/zones.json b/tests/fixtures/tado/zones.json index 8d7265ade50..d85bc9be3ae 100644 --- a/tests/fixtures/tado/zones.json +++ b/tests/fixtures/tado/zones.json @@ -175,5 +175,53 @@ }, "id" : 4, "supportsDazzle" : true + }, + { + "dazzleMode" : { + "supported" : true, + "enabled" : true + }, + "name" : "Air Conditioning with swing", + "id" : 5, + "supportsDazzle" : true, + "devices" : [ + { + "deviceType" : "WR02", + "shortSerialNo" : "WR4", + "serialNo" : "WR4", + "commandTableUploadState" : "FINISHED", + "duties" : [ + "ZONE_UI", + "ZONE_DRIVER", + "ZONE_LEADER" + ], + "currentFwVersion" : "59.4", + "characteristics" : { + "capabilities" : [ + "INSIDE_TEMPERATURE_MEASUREMENT", + "IDENTIFY" + ] + }, + "accessPointWiFi" : { + "ssid" : "tado8480" + }, + "connectionState" : { + "timestamp" : "2020-03-23T18:30:07.377Z", + "value" : true + } + } + ], + "dazzleEnabled" : true, + "dateCreated" : "2019-11-28T15:58:48.968Z", + "openWindowDetection" : { + "timeoutInSeconds" : 900, + "enabled" : true, + "supported" : true + }, + "deviceTypes" : [ + "WR02" + ], + "reportAvailable" : false, + "type" : "AIR_CONDITIONING" } ]