diff --git a/homeassistant/components/tado/climate.py b/homeassistant/components/tado/climate.py index 40bdb19b31b..116985796d5 100644 --- a/homeassistant/components/tado/climate.py +++ b/homeassistant/components/tado/climate.py @@ -73,7 +73,7 @@ from .const import ( TYPE_HEATING, ) from .entity import TadoZoneEntity -from .helper import decide_duration, decide_overlay_mode +from .helper import decide_duration, decide_overlay_mode, generate_supported_fanmodes _LOGGER = logging.getLogger(__name__) @@ -200,15 +200,14 @@ def create_climate_entity( continue if capabilities[mode].get("fanSpeeds"): - supported_fan_modes = [ - TADO_TO_HA_FAN_MODE_MAP_LEGACY[speed] - for speed in capabilities[mode]["fanSpeeds"] - ] + supported_fan_modes = generate_supported_fanmodes( + TADO_TO_HA_FAN_MODE_MAP_LEGACY, capabilities[mode]["fanSpeeds"] + ) + else: - supported_fan_modes = [ - TADO_TO_HA_FAN_MODE_MAP[level] - for level in capabilities[mode]["fanLevel"] - ] + supported_fan_modes = generate_supported_fanmodes( + TADO_TO_HA_FAN_MODE_MAP, capabilities[mode]["fanLevel"] + ) cool_temperatures = capabilities[CONST_MODE_COOL]["temperatures"] else: diff --git a/homeassistant/components/tado/helper.py b/homeassistant/components/tado/helper.py index efcd3e7c4ea..81bff1e36c3 100644 --- a/homeassistant/components/tado/helper.py +++ b/homeassistant/components/tado/helper.py @@ -49,3 +49,15 @@ def decide_duration( ) return duration + + +def generate_supported_fanmodes(tado_to_ha_mapping: dict[str, str], options: list[str]): + """Return correct list of fan modes or None.""" + supported_fanmodes = [ + tado_to_ha_mapping.get(option) + for option in options + if tado_to_ha_mapping.get(option) is not None + ] + if not supported_fanmodes: + return None + return supported_fanmodes diff --git a/tests/components/tado/fixtures/smartac4.with_fanlevel.json b/tests/components/tado/fixtures/smartac4.with_fanlevel.json new file mode 100644 index 00000000000..ea1f9cbd8e5 --- /dev/null +++ b/tests/components/tado/fixtures/smartac4.with_fanlevel.json @@ -0,0 +1,88 @@ +{ + "tadoMode": "HOME", + "geolocationOverride": false, + "geolocationOverrideDisableTime": null, + "preparation": null, + "setting": { + "type": "AIR_CONDITIONING", + "power": "ON", + "mode": "HEAT", + "temperature": { + "celsius": 25.0, + "fahrenheit": 77.0 + }, + "fanLevel": "LEVEL3", + "verticalSwing": "ON", + "horizontalSwing": "ON" + }, + "overlayType": "MANUAL", + "overlay": { + "type": "MANUAL", + "setting": { + "type": "AIR_CONDITIONING", + "power": "ON", + "mode": "HEAT", + "temperature": { + "celsius": 25.0, + "fahrenheit": 77.0 + }, + "fanLevel": "LEVEL3", + "verticalSwing": "ON" + }, + "termination": { + "type": "MANUAL", + "typeSkillBasedApp": "MANUAL", + "projectedExpiry": null + } + }, + "openWindow": null, + "nextScheduleChange": { + "start": "2024-07-01T05: 45: 00Z", + "setting": { + "type": "AIR_CONDITIONING", + "power": "ON", + "mode": "HEAT", + "temperature": { + "celsius": 24.0, + "fahrenheit": 75.2 + }, + "fanLevel": "LEVEL3", + "verticalSwing": "ON", + "horizontalSwing": "ON" + } + }, + "nextTimeBlock": { + "start": "2024-07-01T05: 45: 00.000Z" + }, + "link": { + "state": "ONLINE" + }, + "runningOfflineSchedule": false, + "activityDataPoints": { + "acPower": { + "timestamp": "2022-07-13T18: 06: 58.183Z", + "type": "POWER", + "value": "ON" + } + }, + "sensorDataPoints": { + "insideTemperature": { + "celsius": 24.3, + "fahrenheit": 75.74, + "timestamp": "2024-06-28T22: 23: 15.679Z", + "type": "TEMPERATURE", + "precision": { + "celsius": 0.1, + "fahrenheit": 0.1 + } + }, + "humidity": { + "type": "PERCENTAGE", + "percentage": 70.9, + "timestamp": "2024-06-28T22: 23: 15.679Z" + } + }, + "terminationCondition": { + "type": "MANUAL" + } +} diff --git a/tests/components/tado/fixtures/zone_states.json b/tests/components/tado/fixtures/zone_states.json index 64d457f3b50..df1a99a80f3 100644 --- a/tests/components/tado/fixtures/zone_states.json +++ b/tests/components/tado/fixtures/zone_states.json @@ -287,6 +287,79 @@ "timestamp": "2020-03-28T02:09:27.830Z" } } + }, + "6": { + "tadoMode": "HOME", + "geolocationOverride": false, + "geolocationOverrideDisableTime": null, + "preparation": null, + "setting": { + "type": "AIR_CONDITIONING", + "power": "OFF" + }, + "overlayType": "MANUAL", + "overlay": { + "type": "MANUAL", + "setting": { + "type": "AIR_CONDITIONING", + "power": "OFF" + }, + "termination": { + "type": "MANUAL", + "typeSkillBasedApp": "MANUAL", + "projectedExpiry": null + } + }, + "openWindow": null, + "nextScheduleChange": { + "start": "2024-07-01T05: 45: 00Z", + "setting": { + "type": "AIR_CONDITIONING", + "power": "ON", + "mode": "HEAT", + "temperature": { + "celsius": 24.0, + "fahrenheit": 75.2 + }, + "fanLevel": "LEVEL3", + "verticalSwing": "ON", + "horizontalSwing": "ON" + } + }, + "nextTimeBlock": { + "start": "2024-07-01T05: 45: 00.000Z" + }, + "link": { + "state": "ONLINE" + }, + "runningOfflineSchedule": false, + "activityDataPoints": { + "acPower": { + "timestamp": "2022-07-13T18: 06: 58.183Z", + "type": "POWER", + "value": "OFF" + } + }, + "sensorDataPoints": { + "insideTemperature": { + "celsius": 24.21, + "fahrenheit": 75.58, + "timestamp": "2024-06-28T21: 43: 51.067Z", + "type": "TEMPERATURE", + "precision": { + "celsius": 0.1, + "fahrenheit": 0.1 + } + }, + "humidity": { + "type": "PERCENTAGE", + "percentage": 71.4, + "timestamp": "2024-06-28T21: 43: 51.067Z" + } + }, + "terminationCondition": { + "type": "MANUAL" + } } } } diff --git a/tests/components/tado/fixtures/zone_with_fanlevel_horizontal_vertical_swing.json b/tests/components/tado/fixtures/zone_with_fanlevel_horizontal_vertical_swing.json new file mode 100644 index 00000000000..51ba70b4065 --- /dev/null +++ b/tests/components/tado/fixtures/zone_with_fanlevel_horizontal_vertical_swing.json @@ -0,0 +1,130 @@ +{ + "type": "AIR_CONDITIONING", + "COOL": { + "temperatures": { + "celsius": { + "min": 16, + "max": 31, + "step": 1.0 + }, + "fahrenheit": { + "min": 61, + "max": 88, + "step": 1.0 + } + }, + "fanLevel": ["LEVEL3", "LEVEL2", "AUTO", "LEVEL1", "LEVEL4", "LEVEL5"], + "verticalSwing": ["MID_UP", "MID_DOWN", "ON", "OFF", "UP", "MID", "DOWN"], + "horizontalSwing": ["OFF", "ON"], + "light": ["ON", "OFF"] + }, + "FAN": { + "temperatures": { + "celsius": { + "min": 16, + "max": 31, + "step": 1.0 + }, + "fahrenheit": { + "min": 61, + "max": 88, + "step": 1.0 + } + }, + "fanLevel": ["LEVEL3", "LEVEL2", "AUTO", "LEVEL1", "LEVEL4", "LEVEL5"], + "verticalSwing": ["MID_UP", "MID_DOWN", "ON", "OFF", "UP", "MID", "DOWN"], + "horizontalSwing": ["OFF", "ON"], + "light": ["ON", "OFF"] + }, + "AUTO": { + "fanLevel": ["LEVEL3", "LEVEL2", "AUTO", "LEVEL1", "LEVEL4", "LEVEL5"], + "verticalSwing": ["MID_UP", "MID_DOWN", "ON", "OFF", "UP", "MID", "DOWN"], + "horizontalSwing": ["OFF", "ON"], + "light": ["ON", "OFF"] + }, + "HEAT": { + "temperatures": { + "celsius": { + "min": 16, + "max": 31, + "step": 1.0 + }, + "fahrenheit": { + "min": 61, + "max": 88, + "step": 1.0 + } + }, + "fanLevel": ["LEVEL3", "LEVEL2", "AUTO", "LEVEL1", "LEVEL4", "LEVEL5"], + "verticalSwing": ["MID_UP", "MID_DOWN", "ON", "OFF", "UP", "MID", "DOWN"], + "horizontalSwing": ["OFF", "ON"], + "light": ["ON", "OFF"] + }, + "DRY": { + "temperatures": { + "celsius": { + "min": 16, + "max": 31, + "step": 1.0 + }, + "fahrenheit": { + "min": 61, + "max": 88, + "step": 1.0 + } + }, + "verticalSwing": ["MID_UP", "MID_DOWN", "ON", "OFF", "UP", "MID", "DOWN"], + "horizontalSwing": ["OFF", "ON"], + "light": ["ON", "OFF"] + }, + "initialStates": { + "mode": "COOL", + "modes": { + "COOL": { + "temperature": { + "celsius": 24, + "fahrenheit": 75 + }, + "fanLevel": "LEVEL3", + "verticalSwing": "OFF", + "horizontalSwing": "OFF", + "light": "ON" + }, + "HEAT": { + "temperature": { + "celsius": 24, + "fahrenheit": 75 + }, + "fanLevel": "LEVEL3", + "verticalSwing": "OFF", + "horizontalSwing": "OFF", + "light": "ON" + }, + "DRY": { + "temperature": { + "celsius": 24, + "fahrenheit": 75 + }, + "verticalSwing": "OFF", + "horizontalSwing": "OFF", + "light": "ON" + }, + "FAN": { + "temperature": { + "celsius": 24, + "fahrenheit": 75 + }, + "fanLevel": "LEVEL3", + "verticalSwing": "OFF", + "horizontalSwing": "OFF", + "light": "ON" + }, + "AUTO": { + "fanLevel": "LEVEL3", + "verticalSwing": "OFF", + "horizontalSwing": "OFF", + "light": "ON" + } + } + } +} diff --git a/tests/components/tado/fixtures/zones.json b/tests/components/tado/fixtures/zones.json index 5ef7374a660..e1d2ec759ba 100644 --- a/tests/components/tado/fixtures/zones.json +++ b/tests/components/tado/fixtures/zones.json @@ -178,5 +178,45 @@ "deviceTypes": ["WR02"], "reportAvailable": false, "type": "AIR_CONDITIONING" + }, + { + "id": 6, + "name": "Air Conditioning with fanlevel", + "type": "AIR_CONDITIONING", + "dateCreated": "2022-07-13T18: 06: 58.183Z", + "deviceTypes": ["WR02"], + "devices": [ + { + "deviceType": "WR02", + "serialNo": "WR5", + "shortSerialNo": "WR5", + "currentFwVersion": "118.7", + "connectionState": { + "value": true, + "timestamp": "2024-06-28T21: 04: 23.463Z" + }, + "characteristics": { + "capabilities": ["INSIDE_TEMPERATURE_MEASUREMENT", "IDENTIFY"] + }, + "accessPointWiFi": { + "ssid": "tado8480" + }, + "commandTableUploadState": "FINISHED", + "duties": ["ZONE_UI", "ZONE_DRIVER", "ZONE_LEADER"] + } + ], + "reportAvailable": false, + "showScheduleSetup": false, + "supportsDazzle": true, + "dazzleEnabled": true, + "dazzleMode": { + "supported": true, + "enabled": true + }, + "openWindowDetection": { + "supported": true, + "enabled": true, + "timeoutInSeconds": 900 + } } ] diff --git a/tests/components/tado/test_climate.py b/tests/components/tado/test_climate.py index 98fd2d753a4..5a43c728b6e 100644 --- a/tests/components/tado/test_climate.py +++ b/tests/components/tado/test_climate.py @@ -89,3 +89,35 @@ async def test_smartac_with_swing(hass: HomeAssistant) -> None: # 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_fanlevel_vertical_and_horizontal_swing( + hass: HomeAssistant, +) -> None: + """Test creation of smart ac with swing climate.""" + + await async_init_integration(hass) + + state = hass.states.get("climate.air_conditioning_with_fanlevel") + assert state.state == "heat" + + expected_attributes = { + "current_humidity": 70.9, + "current_temperature": 24.3, + "fan_mode": "high", + "fan_modes": ["high", "medium", "auto", "low"], + "friendly_name": "Air Conditioning with fanlevel", + "hvac_action": "heating", + "hvac_modes": ["off", "auto", "heat", "cool", "heat_cool", "dry", "fan_only"], + "max_temp": 31.0, + "min_temp": 16.0, + "preset_mode": "auto", + "preset_modes": ["away", "home", "auto"], + "swing_modes": ["vertical", "horizontal", "both", "off"], + "supported_features": 441, + "target_temp_step": 1.0, + "temperature": 25.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 dd7c108c984..de4fd515e5a 100644 --- a/tests/components/tado/util.py +++ b/tests/components/tado/util.py @@ -27,6 +27,12 @@ async def async_init_integration( # WR1 Device device_wr1_fixture = "tado/device_wr1.json" + # Smart AC with fanLevel, Vertical and Horizontal swings + zone_6_state_fixture = "tado/smartac4.with_fanlevel.json" + zone_6_capabilities_fixture = ( + "tado/zone_with_fanlevel_horizontal_vertical_swing.json" + ) + # Smart AC with Swing zone_5_state_fixture = "tado/smartac3.with_swing.json" zone_5_capabilities_fixture = "tado/zone_with_swing_capabilities.json" @@ -95,6 +101,10 @@ async def async_init_integration( "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/6/capabilities", + text=load_fixture(zone_6_capabilities_fixture), + ) m.get( "https://my.tado.com/api/v2/homes/1/zones/5/capabilities", text=load_fixture(zone_5_capabilities_fixture), @@ -135,6 +145,14 @@ async def async_init_integration( "https://my.tado.com/api/v2/homes/1/zones/5/defaultOverlay", text=load_fixture(zone_def_overlay), ) + m.get( + "https://my.tado.com/api/v2/homes/1/zones/6/defaultOverlay", + text=load_fixture(zone_def_overlay), + ) + m.get( + "https://my.tado.com/api/v2/homes/1/zones/6/state", + text=load_fixture(zone_6_state_fixture), + ) m.get( "https://my.tado.com/api/v2/homes/1/zones/5/state", text=load_fixture(zone_5_state_fixture),