diff --git a/homeassistant/components/daikin/climate.py b/homeassistant/components/daikin/climate.py index 601f5df569dc..d76644c6bc2c 100644 --- a/homeassistant/components/daikin/climate.py +++ b/homeassistant/components/daikin/climate.py @@ -416,8 +416,11 @@ class DaikinZoneClimate(DaikinEntity, ClimateEntity): @property @override def hvac_modes(self) -> list[HVACMode]: - """Return the hvac modes (mirrors the main unit).""" - return [self._main_hvac_mode] + """Return the main unit HVAC mode and zone power off.""" + main_mode = self._main_hvac_mode + if main_mode == HVACMode.OFF: + return [HVACMode.OFF] + return [main_mode, HVACMode.OFF] @property @override @@ -549,7 +552,13 @@ class DaikinZoneClimate(DaikinEntity, ClimateEntity): @override async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: - """Disallow changing HVAC mode via zone climate.""" + """Set zone power without changing the main unit HVAC mode.""" + if hvac_mode == HVACMode.OFF: + await self.async_turn_off() + return + if hvac_mode == self._main_hvac_mode: + await self.async_turn_on() + return raise HomeAssistantError( translation_domain=DOMAIN, translation_key="zone_hvac_read_only", diff --git a/tests/components/daikin/test_zone_climate.py b/tests/components/daikin/test_zone_climate.py index 2ca160f8a768..78515736f263 100644 --- a/tests/components/daikin/test_zone_climate.py +++ b/tests/components/daikin/test_zone_climate.py @@ -279,29 +279,31 @@ async def test_zone_climate_parameters_unavailable( assert state.state == STATE_UNAVAILABLE -async def test_zone_climate_hvac_modes_read_only( +async def test_zone_climate_rejects_main_mode_change( hass: HomeAssistant, entity_registry: er.EntityRegistry, zone_device: ZoneDevice, ) -> None: - """Changing HVAC mode through a zone climate is blocked.""" + """Changing the main HVAC mode through a zone climate is blocked.""" configure_zone_device(zone_device, zones=[["Living", "1", 22]]) await _async_setup_daikin(hass, zone_device) entity_id = _zone_entity_id(entity_registry, zone_device, 0) assert entity_id is not None - with pytest.raises(HomeAssistantError) as err: + with pytest.raises(ServiceValidationError) as err: await hass.services.async_call( CLIMATE_DOMAIN, SERVICE_SET_HVAC_MODE, { ATTR_ENTITY_ID: entity_id, - ATTR_HVAC_MODE: HVACMode.HEAT, + ATTR_HVAC_MODE: HVACMode.COOL, }, blocking=True, ) - assert err.value.translation_key == "zone_hvac_read_only" + assert err.value.translation_key == "not_valid_hvac_mode" + zone_device.set_zone.assert_not_awaited() + zone_device.set.assert_not_awaited() async def test_zone_climate_set_temperature_requires_heat_or_cool( @@ -360,7 +362,7 @@ async def test_zone_climate_properties( assert state.attributes[ATTR_TEMPERATURE] == 18.0 assert state.attributes[ATTR_MIN_TEMP] == 22.0 assert state.attributes[ATTR_MAX_TEMP] == 26.0 - assert state.attributes[ATTR_HVAC_MODES] == [HVACMode.COOL] + assert state.attributes[ATTR_HVAC_MODES] == [HVACMode.COOL, HVACMode.OFF] assert state.attributes[ATTR_SUPPORTED_FEATURES] == ( ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.TURN_OFF @@ -377,6 +379,7 @@ async def test_zone_climate_properties( "expected_climate_state", "expected_switch_state", "main_mode", + "service_data", ), [ pytest.param( @@ -386,6 +389,7 @@ async def test_zone_climate_properties( HVACMode.COOL, STATE_ON, "cool", + {}, id="climate-turn-on", ), pytest.param( @@ -395,6 +399,7 @@ async def test_zone_climate_properties( HVACMode.OFF, STATE_OFF, "cool", + {}, id="climate-turn-off", ), pytest.param( @@ -404,6 +409,7 @@ async def test_zone_climate_properties( HVACMode.OFF, STATE_OFF, "cool", + {}, id="climate-toggle-off", ), pytest.param( @@ -413,8 +419,29 @@ async def test_zone_climate_properties( HVACMode.OFF, STATE_OFF, "off", + {}, id="climate-toggle-zone-on-main-off", ), + pytest.param( + SERVICE_SET_HVAC_MODE, + "0", + "1", + HVACMode.HEAT, + STATE_ON, + "hot", + {ATTR_HVAC_MODE: HVACMode.HEAT}, + id="hvac-mode-turn-on", + ), + pytest.param( + SERVICE_SET_HVAC_MODE, + "1", + "0", + HVACMode.OFF, + STATE_OFF, + "hot", + {ATTR_HVAC_MODE: HVACMode.OFF}, + id="hvac-mode-turn-off", + ), ], ) async def test_zone_climate_power_controls( @@ -427,6 +454,7 @@ async def test_zone_climate_power_controls( expected_climate_state: HVACMode, expected_switch_state: str, main_mode: str, + service_data: dict[str, HVACMode], ) -> None: """Zone climate and switch power controls stay synchronized.""" configure_zone_device( @@ -449,7 +477,7 @@ async def test_zone_climate_power_controls( await hass.services.async_call( CLIMATE_DOMAIN, service, - {ATTR_ENTITY_ID: climate_entity_id}, + {ATTR_ENTITY_ID: climate_entity_id, **service_data}, blocking=True, ) @@ -504,10 +532,15 @@ async def test_zone_switch_updates_zone_climate( @pytest.mark.parametrize( - ("mode", "expected_state"), + ("mode", "expected_state", "expected_modes"), [ - pytest.param("auto", HVACMode.HEAT_COOL, id="auto"), - pytest.param("off", HVACMode.OFF, id="off"), + pytest.param( + "auto", + HVACMode.HEAT_COOL, + [HVACMode.HEAT_COOL, HVACMode.OFF], + id="auto", + ), + pytest.param("off", HVACMode.OFF, [HVACMode.OFF], id="off"), ], ) async def test_zone_climate_target_temperature_inactive_mode( @@ -516,6 +549,7 @@ async def test_zone_climate_target_temperature_inactive_mode( zone_device: ZoneDevice, mode: str, expected_state: HVACMode, + expected_modes: list[HVACMode], ) -> None: """In non-heating/cooling modes, zone target temperature is None.""" configure_zone_device( @@ -532,6 +566,7 @@ async def test_zone_climate_target_temperature_inactive_mode( state = hass.states.get(entity_id) assert state is not None assert state.state == expected_state + assert state.attributes[ATTR_HVAC_MODES] == expected_modes assert state.attributes[ATTR_TEMPERATURE] is None