mirror of
https://github.com/home-assistant/core.git
synced 2026-09-25 07:51:46 -05:00
Follow the requested mode when setting a SwitchBot Cloud AC temperature (#180518)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: balloob <1444314+balloob@users.noreply.github.com>
This commit is contained in:
co-authored by
copilot-swe-agent[bot]
balloob
parent
91ba35fd1e
commit
317e87d130
@@ -15,6 +15,7 @@ from switchbot_api import (
|
||||
|
||||
from homeassistant.components import climate as FanState
|
||||
from homeassistant.components.climate import (
|
||||
ATTR_HVAC_MODE,
|
||||
ATTR_TEMPERATURE,
|
||||
PRESET_BOOST,
|
||||
PRESET_COMFORT,
|
||||
@@ -149,18 +150,20 @@ class SwitchBotCloudAirConditioner(SwitchBotCloudEntity, ClimateEntity, RestoreE
|
||||
if self.min_temp <= temperature <= self.max_temp:
|
||||
self._attr_target_temperature = temperature
|
||||
|
||||
def _get_mode(self, hvac_mode: HVACMode | None) -> int:
|
||||
new_hvac_mode = hvac_mode or self._attr_hvac_mode
|
||||
def _get_mode(self, hvac_mode: HVACMode) -> int:
|
||||
"""Return the SwitchBot mode for the command.
|
||||
|
||||
Every command carries a mode, so one that turns the device off carries
|
||||
the mode it was last running in.
|
||||
"""
|
||||
_LOGGER.debug(
|
||||
"Received hvac_mode: %s (Currently set as %s)",
|
||||
"Resolving mode for hvac_mode: %s (Currently set as %s)",
|
||||
hvac_mode,
|
||||
self._attr_hvac_mode,
|
||||
)
|
||||
if new_hvac_mode == HVACMode.OFF:
|
||||
return _SWITCHBOT_HVAC_MODES.get(
|
||||
self._attr_hvac_mode, _DEFAULT_SWITCHBOT_HVAC_MODE
|
||||
)
|
||||
return _SWITCHBOT_HVAC_MODES.get(new_hvac_mode, _DEFAULT_SWITCHBOT_HVAC_MODE)
|
||||
if hvac_mode == HVACMode.OFF:
|
||||
hvac_mode = self._attr_hvac_mode
|
||||
return _SWITCHBOT_HVAC_MODES.get(hvac_mode, _DEFAULT_SWITCHBOT_HVAC_MODE)
|
||||
|
||||
async def _do_send_command(
|
||||
self,
|
||||
@@ -169,11 +172,14 @@ class SwitchBotCloudAirConditioner(SwitchBotCloudEntity, ClimateEntity, RestoreE
|
||||
temperature: float | None = None,
|
||||
) -> None:
|
||||
new_temperature = temperature or self._attr_target_temperature
|
||||
new_mode = self._get_mode(hvac_mode)
|
||||
# A command without a mode of its own follows the mode the entity is
|
||||
# already in, so it cannot power a device on that is off
|
||||
new_hvac_mode = hvac_mode or self._attr_hvac_mode
|
||||
new_mode = self._get_mode(new_hvac_mode)
|
||||
new_fan_speed = _SWITCHBOT_FAN_MODES.get(
|
||||
fan_mode or self._attr_fan_mode, _DEFAULT_SWITCHBOT_FAN_MODE
|
||||
)
|
||||
new_power_state = "on" if hvac_mode != HVACMode.OFF else "off"
|
||||
new_power_state = "on" if new_hvac_mode != HVACMode.OFF else "off"
|
||||
command = f"{int(new_temperature)},{new_mode},{new_fan_speed},{new_power_state}"
|
||||
_LOGGER.debug("Sending command to %s: %s", self._attr_unique_id, command)
|
||||
await self.send_api_command(
|
||||
@@ -200,8 +206,14 @@ class SwitchBotCloudAirConditioner(SwitchBotCloudEntity, ClimateEntity, RestoreE
|
||||
"""Set target temperature."""
|
||||
if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None:
|
||||
return
|
||||
await self._do_send_command(temperature=temperature)
|
||||
hvac_mode: HVACMode | None = kwargs.get(ATTR_HVAC_MODE)
|
||||
if hvac_mode is not None:
|
||||
self._valid_mode_or_raise("hvac", hvac_mode, self.hvac_modes)
|
||||
|
||||
await self._do_send_command(hvac_mode=hvac_mode, temperature=temperature)
|
||||
self._attr_target_temperature = temperature
|
||||
if hvac_mode is not None:
|
||||
self._attr_hvac_mode = hvac_mode
|
||||
self.async_write_ha_state()
|
||||
|
||||
@override
|
||||
|
||||
@@ -21,6 +21,7 @@ from homeassistant.components.climate import (
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant, State
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM
|
||||
|
||||
from . import configure_integration
|
||||
@@ -132,6 +133,101 @@ async def test_air_conditioner_set_temperature(
|
||||
assert hass.states.get(entity_id).attributes[ATTR_TEMPERATURE] == 25
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("service_data", "expected_command", "expected_state"),
|
||||
[
|
||||
pytest.param(
|
||||
{ATTR_TEMPERATURE: 27, ATTR_HVAC_MODE: HVACMode.DRY},
|
||||
"27,3,1,on",
|
||||
HVACMode.DRY,
|
||||
id="with_hvac_mode",
|
||||
),
|
||||
pytest.param(
|
||||
{ATTR_TEMPERATURE: 27},
|
||||
"27,4,1,off",
|
||||
HVACMode.OFF,
|
||||
id="without_hvac_mode",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_air_conditioner_set_temperature_while_off(
|
||||
hass: HomeAssistant,
|
||||
mock_list_devices: AsyncMock,
|
||||
mock_get_status: AsyncMock,
|
||||
service_data: dict[str, float | HVACMode],
|
||||
expected_command: str,
|
||||
expected_state: HVACMode,
|
||||
) -> None:
|
||||
"""Test setting the temperature of an air conditioner that is off."""
|
||||
mock_list_devices.return_value = [
|
||||
Remote(
|
||||
deviceId="ac-device-id-1",
|
||||
deviceName="climate-1",
|
||||
remoteType="Air Conditioner",
|
||||
hubDeviceId="test-hub-id",
|
||||
),
|
||||
]
|
||||
|
||||
entity_id = "climate.climate_1"
|
||||
mock_restore_cache(hass, (State(entity_id, HVACMode.OFF),))
|
||||
|
||||
entry = await configure_integration(hass)
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
assert hass.states.get(entity_id).state == HVACMode.OFF
|
||||
|
||||
with patch.object(SwitchBotAPI, "send_command") as mock_send_command:
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
SERVICE_SET_TEMPERATURE,
|
||||
{ATTR_ENTITY_ID: entity_id, **service_data},
|
||||
blocking=True,
|
||||
)
|
||||
mock_send_command.assert_called_once()
|
||||
assert expected_command in str(mock_send_command.call_args)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state.state == expected_state
|
||||
assert state.attributes[ATTR_TEMPERATURE] == 27
|
||||
|
||||
|
||||
async def test_air_conditioner_set_temperature_unsupported_hvac_mode(
|
||||
hass: HomeAssistant,
|
||||
mock_list_devices: AsyncMock,
|
||||
mock_get_status: AsyncMock,
|
||||
) -> None:
|
||||
"""Test setting the temperature with an hvac mode the air conditioner lacks."""
|
||||
mock_list_devices.return_value = [
|
||||
Remote(
|
||||
deviceId="ac-device-id-1",
|
||||
deviceName="climate-1",
|
||||
remoteType="Air Conditioner",
|
||||
hubDeviceId="test-hub-id",
|
||||
),
|
||||
]
|
||||
|
||||
entry = await configure_integration(hass)
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
entity_id = "climate.climate_1"
|
||||
|
||||
with (
|
||||
patch.object(SwitchBotAPI, "send_command") as mock_send_command,
|
||||
pytest.raises(ServiceValidationError),
|
||||
):
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
SERVICE_SET_TEMPERATURE,
|
||||
{
|
||||
ATTR_ENTITY_ID: entity_id,
|
||||
ATTR_TEMPERATURE: 27,
|
||||
ATTR_HVAC_MODE: HVACMode.AUTO,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_send_command.assert_not_called()
|
||||
assert hass.states.get(entity_id).state == HVACMode.FAN_ONLY
|
||||
|
||||
|
||||
async def test_air_conditioner_restore_state(
|
||||
hass: HomeAssistant, mock_list_devices, mock_get_status
|
||||
) -> None:
|
||||
|
||||
Reference in New Issue
Block a user