mirror of
https://github.com/home-assistant/core.git
synced 2026-09-25 07:51:46 -05:00
Add Switchbot Cloud AC Off (#138648)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
co-authored by
Joost Lekkerkerker
Erik Montnemery
parent
3b60961f02
commit
095f73d84f
@@ -1,24 +1,30 @@
|
||||
"""Support for SwitchBot Air Conditioner remotes."""
|
||||
|
||||
from logging import getLogger
|
||||
from typing import Any
|
||||
|
||||
from switchbot_api import AirConditionerCommands
|
||||
|
||||
from homeassistant.components import climate as FanState
|
||||
from homeassistant.components.climate import (
|
||||
ATTR_FAN_MODE,
|
||||
ATTR_TEMPERATURE,
|
||||
ClimateEntity,
|
||||
ClimateEntityFeature,
|
||||
HVACMode,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
|
||||
from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN, UnitOfTemperature
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
|
||||
from . import SwitchbotCloudData
|
||||
from .const import DOMAIN
|
||||
from .entity import SwitchBotCloudEntity
|
||||
|
||||
_LOGGER = getLogger(__name__)
|
||||
|
||||
_SWITCHBOT_HVAC_MODES: dict[HVACMode, int] = {
|
||||
HVACMode.HEAT_COOL: 1,
|
||||
HVACMode.COOL: 2,
|
||||
@@ -52,7 +58,7 @@ async def async_setup_entry(
|
||||
)
|
||||
|
||||
|
||||
class SwitchBotCloudAirConditioner(SwitchBotCloudEntity, ClimateEntity):
|
||||
class SwitchBotCloudAirConditioner(SwitchBotCloudEntity, ClimateEntity, RestoreEntity):
|
||||
"""Representation of a SwitchBot air conditioner.
|
||||
|
||||
As it is an IR device, we don't know the actual state.
|
||||
@@ -75,6 +81,7 @@ class SwitchBotCloudAirConditioner(SwitchBotCloudEntity, ClimateEntity):
|
||||
HVACMode.DRY,
|
||||
HVACMode.FAN_ONLY,
|
||||
HVACMode.HEAT,
|
||||
HVACMode.OFF,
|
||||
]
|
||||
_attr_hvac_mode = HVACMode.FAN_ONLY
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
@@ -83,6 +90,39 @@ class SwitchBotCloudAirConditioner(SwitchBotCloudEntity, ClimateEntity):
|
||||
_attr_precision = 1
|
||||
_attr_name = None
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Run when entity about to be added."""
|
||||
await super().async_added_to_hass()
|
||||
|
||||
if not (
|
||||
last_state := await self.async_get_last_state()
|
||||
) or last_state.state in (
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
):
|
||||
return
|
||||
_LOGGER.debug("Last state attributes: %s", last_state.attributes)
|
||||
self._attr_hvac_mode = HVACMode(last_state.state)
|
||||
self._attr_fan_mode = last_state.attributes.get(
|
||||
ATTR_FAN_MODE, self._attr_fan_mode
|
||||
)
|
||||
self._attr_target_temperature = last_state.attributes.get(
|
||||
ATTR_TEMPERATURE, self._attr_target_temperature
|
||||
)
|
||||
|
||||
def _get_mode(self, hvac_mode: HVACMode | None) -> int:
|
||||
new_hvac_mode = hvac_mode or self._attr_hvac_mode
|
||||
_LOGGER.debug(
|
||||
"Received 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)
|
||||
|
||||
async def _do_send_command(
|
||||
self,
|
||||
hvac_mode: HVACMode | None = None,
|
||||
@@ -90,15 +130,16 @@ class SwitchBotCloudAirConditioner(SwitchBotCloudEntity, ClimateEntity):
|
||||
temperature: float | None = None,
|
||||
) -> None:
|
||||
new_temperature = temperature or self._attr_target_temperature
|
||||
new_mode = _SWITCHBOT_HVAC_MODES.get(
|
||||
hvac_mode or self._attr_hvac_mode, _DEFAULT_SWITCHBOT_HVAC_MODE
|
||||
)
|
||||
new_mode = self._get_mode(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"
|
||||
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(
|
||||
AirConditionerCommands.SET_ALL,
|
||||
parameters=f"{int(new_temperature)},{new_mode},{new_fan_speed},on",
|
||||
parameters=command,
|
||||
)
|
||||
|
||||
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
"""Test for the switchbot_cloud climate."""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from switchbot_api import Remote
|
||||
|
||||
from homeassistant.components.climate import (
|
||||
ATTR_FAN_MODE,
|
||||
ATTR_HVAC_MODE,
|
||||
ATTR_TEMPERATURE,
|
||||
DOMAIN as CLIMATE_DOMAIN,
|
||||
SERVICE_SET_FAN_MODE,
|
||||
SERVICE_SET_HVAC_MODE,
|
||||
SERVICE_SET_TEMPERATURE,
|
||||
)
|
||||
from homeassistant.components.switchbot_cloud import SwitchBotAPI
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant, State
|
||||
|
||||
from . import configure_integration
|
||||
|
||||
from tests.common import mock_restore_cache
|
||||
|
||||
|
||||
async def test_air_conditioner_set_hvac_mode(
|
||||
hass: HomeAssistant, mock_list_devices, mock_get_status
|
||||
) -> None:
|
||||
"""Test setting HVAC mode for air conditioner."""
|
||||
mock_list_devices.return_value = [
|
||||
Remote(
|
||||
deviceId="ac-device-id-1",
|
||||
deviceName="climate-1",
|
||||
remoteType="DIY 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:
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
SERVICE_SET_HVAC_MODE,
|
||||
{ATTR_ENTITY_ID: entity_id, ATTR_HVAC_MODE: "cool"},
|
||||
blocking=True,
|
||||
)
|
||||
mock_send_command.assert_called_once()
|
||||
assert "21,2,1,on" in str(mock_send_command.call_args)
|
||||
|
||||
assert hass.states.get(entity_id).state == "cool"
|
||||
|
||||
# Test turning off
|
||||
with patch.object(SwitchBotAPI, "send_command") as mock_send_command:
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
SERVICE_SET_HVAC_MODE,
|
||||
{ATTR_ENTITY_ID: entity_id, ATTR_HVAC_MODE: "off"},
|
||||
blocking=True,
|
||||
)
|
||||
mock_send_command.assert_called_once()
|
||||
assert "21,2,1,off" in str(mock_send_command.call_args)
|
||||
|
||||
assert hass.states.get(entity_id).state == "off"
|
||||
|
||||
|
||||
async def test_air_conditioner_set_fan_mode(
|
||||
hass: HomeAssistant, mock_list_devices, mock_get_status
|
||||
) -> None:
|
||||
"""Test setting fan mode for air conditioner."""
|
||||
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:
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
SERVICE_SET_FAN_MODE,
|
||||
{ATTR_ENTITY_ID: entity_id, ATTR_FAN_MODE: "high"},
|
||||
blocking=True,
|
||||
)
|
||||
mock_send_command.assert_called_once()
|
||||
assert "21,4,4,on" in str(mock_send_command.call_args)
|
||||
|
||||
assert hass.states.get(entity_id).attributes[ATTR_FAN_MODE] == "high"
|
||||
|
||||
|
||||
async def test_air_conditioner_set_temperature(
|
||||
hass: HomeAssistant, mock_list_devices, mock_get_status
|
||||
) -> None:
|
||||
"""Test setting temperature for air conditioner."""
|
||||
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:
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
SERVICE_SET_TEMPERATURE,
|
||||
{ATTR_ENTITY_ID: entity_id, ATTR_TEMPERATURE: 25},
|
||||
blocking=True,
|
||||
)
|
||||
mock_send_command.assert_called_once()
|
||||
assert "25,4,1,on" in str(mock_send_command.call_args)
|
||||
|
||||
assert hass.states.get(entity_id).attributes[ATTR_TEMPERATURE] == 25
|
||||
|
||||
|
||||
async def test_air_conditioner_restore_state(
|
||||
hass: HomeAssistant, mock_list_devices, mock_get_status
|
||||
) -> None:
|
||||
"""Test restoring state for air conditioner."""
|
||||
mock_list_devices.return_value = [
|
||||
Remote(
|
||||
deviceId="ac-device-id-1",
|
||||
deviceName="climate-1",
|
||||
remoteType="Air Conditioner",
|
||||
hubDeviceId="test-hub-id",
|
||||
),
|
||||
]
|
||||
|
||||
mock_state = State(
|
||||
"climate.climate_1",
|
||||
"cool",
|
||||
{
|
||||
ATTR_FAN_MODE: "high",
|
||||
ATTR_TEMPERATURE: 25,
|
||||
},
|
||||
)
|
||||
|
||||
mock_restore_cache(hass, (mock_state,))
|
||||
entry = await configure_integration(hass)
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
entity_id = "climate.climate_1"
|
||||
state = hass.states.get(entity_id)
|
||||
assert state.state == "cool"
|
||||
assert state.attributes[ATTR_FAN_MODE] == "high"
|
||||
assert state.attributes[ATTR_TEMPERATURE] == 25
|
||||
|
||||
|
||||
async def test_air_conditioner_no_last_state(
|
||||
hass: HomeAssistant, mock_list_devices, mock_get_status
|
||||
) -> None:
|
||||
"""Test behavior when no previous state exists."""
|
||||
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"
|
||||
state = hass.states.get(entity_id)
|
||||
assert state.state == "fan_only"
|
||||
assert state.attributes[ATTR_FAN_MODE] == "auto"
|
||||
assert state.attributes[ATTR_TEMPERATURE] == 21
|
||||
Reference in New Issue
Block a user