mirror of https://github.com/home-assistant/core
487 lines
15 KiB
Python
487 lines
15 KiB
Python
"""Test ESPHome climates."""
|
|
|
|
import math
|
|
from unittest.mock import call
|
|
|
|
from aioesphomeapi import (
|
|
APIClient,
|
|
ClimateAction,
|
|
ClimateFanMode,
|
|
ClimateInfo,
|
|
ClimateMode,
|
|
ClimatePreset,
|
|
ClimateState,
|
|
ClimateSwingMode,
|
|
)
|
|
import pytest
|
|
from syrupy import SnapshotAssertion
|
|
|
|
from homeassistant.components.climate import (
|
|
ATTR_CURRENT_HUMIDITY,
|
|
ATTR_CURRENT_TEMPERATURE,
|
|
ATTR_FAN_MODE,
|
|
ATTR_HUMIDITY,
|
|
ATTR_HVAC_MODE,
|
|
ATTR_MAX_HUMIDITY,
|
|
ATTR_MIN_HUMIDITY,
|
|
ATTR_PRESET_MODE,
|
|
ATTR_SWING_MODE,
|
|
ATTR_TARGET_TEMP_HIGH,
|
|
ATTR_TARGET_TEMP_LOW,
|
|
ATTR_TEMPERATURE,
|
|
DOMAIN as CLIMATE_DOMAIN,
|
|
FAN_HIGH,
|
|
SERVICE_SET_FAN_MODE,
|
|
SERVICE_SET_HUMIDITY,
|
|
SERVICE_SET_HVAC_MODE,
|
|
SERVICE_SET_PRESET_MODE,
|
|
SERVICE_SET_SWING_MODE,
|
|
SERVICE_SET_TEMPERATURE,
|
|
SWING_BOTH,
|
|
HVACMode,
|
|
)
|
|
from homeassistant.const import ATTR_ENTITY_ID
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.exceptions import ServiceValidationError
|
|
|
|
|
|
async def test_climate_entity(
|
|
hass: HomeAssistant, mock_client: APIClient, mock_generic_device_entry
|
|
) -> None:
|
|
"""Test a generic climate entity."""
|
|
entity_info = [
|
|
ClimateInfo(
|
|
object_id="myclimate",
|
|
key=1,
|
|
name="my climate",
|
|
unique_id="my_climate",
|
|
supports_current_temperature=True,
|
|
supports_action=True,
|
|
visual_min_temperature=10.0,
|
|
visual_max_temperature=30.0,
|
|
)
|
|
]
|
|
states = [
|
|
ClimateState(
|
|
key=1,
|
|
mode=ClimateMode.COOL,
|
|
action=ClimateAction.COOLING,
|
|
current_temperature=30,
|
|
target_temperature=20,
|
|
fan_mode=ClimateFanMode.AUTO,
|
|
swing_mode=ClimateSwingMode.BOTH,
|
|
)
|
|
]
|
|
user_service = []
|
|
await mock_generic_device_entry(
|
|
mock_client=mock_client,
|
|
entity_info=entity_info,
|
|
user_service=user_service,
|
|
states=states,
|
|
)
|
|
state = hass.states.get("climate.test_myclimate")
|
|
assert state is not None
|
|
assert state.state == HVACMode.COOL
|
|
|
|
await hass.services.async_call(
|
|
CLIMATE_DOMAIN,
|
|
SERVICE_SET_TEMPERATURE,
|
|
{ATTR_ENTITY_ID: "climate.test_myclimate", ATTR_TEMPERATURE: 25},
|
|
blocking=True,
|
|
)
|
|
mock_client.climate_command.assert_has_calls([call(key=1, target_temperature=25.0)])
|
|
mock_client.climate_command.reset_mock()
|
|
|
|
|
|
async def test_climate_entity_with_step_and_two_point(
|
|
hass: HomeAssistant, mock_client: APIClient, mock_generic_device_entry
|
|
) -> None:
|
|
"""Test a generic climate entity."""
|
|
entity_info = [
|
|
ClimateInfo(
|
|
object_id="myclimate",
|
|
key=1,
|
|
name="my climate",
|
|
unique_id="my_climate",
|
|
supports_current_temperature=True,
|
|
supports_two_point_target_temperature=True,
|
|
visual_target_temperature_step=2,
|
|
visual_current_temperature_step=2,
|
|
supports_action=False,
|
|
visual_min_temperature=10.0,
|
|
visual_max_temperature=30.0,
|
|
supported_modes=[ClimateMode.COOL, ClimateMode.HEAT, ClimateMode.AUTO],
|
|
supported_presets=[ClimatePreset.AWAY, ClimatePreset.ACTIVITY],
|
|
)
|
|
]
|
|
states = [
|
|
ClimateState(
|
|
key=1,
|
|
mode=ClimateMode.COOL,
|
|
current_temperature=30,
|
|
target_temperature=20,
|
|
fan_mode=ClimateFanMode.AUTO,
|
|
swing_mode=ClimateSwingMode.BOTH,
|
|
)
|
|
]
|
|
user_service = []
|
|
await mock_generic_device_entry(
|
|
mock_client=mock_client,
|
|
entity_info=entity_info,
|
|
user_service=user_service,
|
|
states=states,
|
|
)
|
|
state = hass.states.get("climate.test_myclimate")
|
|
assert state is not None
|
|
assert state.state == HVACMode.COOL
|
|
|
|
with pytest.raises(ServiceValidationError):
|
|
await hass.services.async_call(
|
|
CLIMATE_DOMAIN,
|
|
SERVICE_SET_TEMPERATURE,
|
|
{ATTR_ENTITY_ID: "climate.test_myclimate", ATTR_TEMPERATURE: 25},
|
|
blocking=True,
|
|
)
|
|
|
|
await hass.services.async_call(
|
|
CLIMATE_DOMAIN,
|
|
SERVICE_SET_TEMPERATURE,
|
|
{
|
|
ATTR_ENTITY_ID: "climate.test_myclimate",
|
|
ATTR_HVAC_MODE: HVACMode.AUTO,
|
|
ATTR_TARGET_TEMP_LOW: 20,
|
|
ATTR_TARGET_TEMP_HIGH: 30,
|
|
},
|
|
blocking=True,
|
|
)
|
|
mock_client.climate_command.assert_has_calls(
|
|
[
|
|
call(
|
|
key=1,
|
|
mode=ClimateMode.AUTO,
|
|
target_temperature_low=20.0,
|
|
target_temperature_high=30.0,
|
|
)
|
|
]
|
|
)
|
|
mock_client.climate_command.reset_mock()
|
|
|
|
|
|
async def test_climate_entity_with_step_and_target_temp(
|
|
hass: HomeAssistant, mock_client: APIClient, mock_generic_device_entry
|
|
) -> None:
|
|
"""Test a generic climate entity."""
|
|
entity_info = [
|
|
ClimateInfo(
|
|
object_id="myclimate",
|
|
key=1,
|
|
name="my climate",
|
|
unique_id="my_climate",
|
|
supports_current_temperature=True,
|
|
visual_target_temperature_step=2,
|
|
visual_current_temperature_step=2,
|
|
supports_action=True,
|
|
visual_min_temperature=10.0,
|
|
visual_max_temperature=30.0,
|
|
supported_fan_modes=[ClimateFanMode.LOW, ClimateFanMode.HIGH],
|
|
supported_modes=[ClimateMode.COOL, ClimateMode.HEAT, ClimateMode.AUTO],
|
|
supported_presets=[ClimatePreset.AWAY, ClimatePreset.ACTIVITY],
|
|
supported_custom_presets=["preset1", "preset2"],
|
|
supported_custom_fan_modes=["fan1", "fan2"],
|
|
supported_swing_modes=[ClimateSwingMode.BOTH, ClimateSwingMode.OFF],
|
|
)
|
|
]
|
|
states = [
|
|
ClimateState(
|
|
key=1,
|
|
mode=ClimateMode.COOL,
|
|
action=ClimateAction.COOLING,
|
|
current_temperature=30,
|
|
target_temperature=20,
|
|
fan_mode=ClimateFanMode.AUTO,
|
|
swing_mode=ClimateSwingMode.BOTH,
|
|
)
|
|
]
|
|
user_service = []
|
|
await mock_generic_device_entry(
|
|
mock_client=mock_client,
|
|
entity_info=entity_info,
|
|
user_service=user_service,
|
|
states=states,
|
|
)
|
|
state = hass.states.get("climate.test_myclimate")
|
|
assert state is not None
|
|
assert state.state == HVACMode.COOL
|
|
|
|
await hass.services.async_call(
|
|
CLIMATE_DOMAIN,
|
|
SERVICE_SET_TEMPERATURE,
|
|
{
|
|
ATTR_ENTITY_ID: "climate.test_myclimate",
|
|
ATTR_HVAC_MODE: HVACMode.AUTO,
|
|
ATTR_TEMPERATURE: 25,
|
|
},
|
|
blocking=True,
|
|
)
|
|
mock_client.climate_command.assert_has_calls(
|
|
[call(key=1, mode=ClimateMode.AUTO, target_temperature=25.0)]
|
|
)
|
|
mock_client.climate_command.reset_mock()
|
|
|
|
with pytest.raises(ServiceValidationError):
|
|
await hass.services.async_call(
|
|
CLIMATE_DOMAIN,
|
|
SERVICE_SET_TEMPERATURE,
|
|
{
|
|
ATTR_ENTITY_ID: "climate.test_myclimate",
|
|
ATTR_HVAC_MODE: HVACMode.AUTO,
|
|
ATTR_TARGET_TEMP_LOW: 20,
|
|
ATTR_TARGET_TEMP_HIGH: 30,
|
|
},
|
|
blocking=True,
|
|
)
|
|
|
|
await hass.services.async_call(
|
|
CLIMATE_DOMAIN,
|
|
SERVICE_SET_HVAC_MODE,
|
|
{
|
|
ATTR_ENTITY_ID: "climate.test_myclimate",
|
|
ATTR_HVAC_MODE: HVACMode.HEAT,
|
|
},
|
|
blocking=True,
|
|
)
|
|
mock_client.climate_command.assert_has_calls(
|
|
[
|
|
call(
|
|
key=1,
|
|
mode=ClimateMode.HEAT,
|
|
)
|
|
]
|
|
)
|
|
mock_client.climate_command.reset_mock()
|
|
|
|
await hass.services.async_call(
|
|
CLIMATE_DOMAIN,
|
|
SERVICE_SET_PRESET_MODE,
|
|
{ATTR_ENTITY_ID: "climate.test_myclimate", ATTR_PRESET_MODE: "away"},
|
|
blocking=True,
|
|
)
|
|
mock_client.climate_command.assert_has_calls(
|
|
[
|
|
call(
|
|
key=1,
|
|
preset=ClimatePreset.AWAY,
|
|
)
|
|
]
|
|
)
|
|
mock_client.climate_command.reset_mock()
|
|
|
|
await hass.services.async_call(
|
|
CLIMATE_DOMAIN,
|
|
SERVICE_SET_PRESET_MODE,
|
|
{ATTR_ENTITY_ID: "climate.test_myclimate", ATTR_PRESET_MODE: "preset1"},
|
|
blocking=True,
|
|
)
|
|
mock_client.climate_command.assert_has_calls([call(key=1, custom_preset="preset1")])
|
|
mock_client.climate_command.reset_mock()
|
|
|
|
await hass.services.async_call(
|
|
CLIMATE_DOMAIN,
|
|
SERVICE_SET_FAN_MODE,
|
|
{ATTR_ENTITY_ID: "climate.test_myclimate", ATTR_FAN_MODE: FAN_HIGH},
|
|
blocking=True,
|
|
)
|
|
mock_client.climate_command.assert_has_calls(
|
|
[call(key=1, fan_mode=ClimateFanMode.HIGH)]
|
|
)
|
|
mock_client.climate_command.reset_mock()
|
|
|
|
await hass.services.async_call(
|
|
CLIMATE_DOMAIN,
|
|
SERVICE_SET_FAN_MODE,
|
|
{ATTR_ENTITY_ID: "climate.test_myclimate", ATTR_FAN_MODE: "fan2"},
|
|
blocking=True,
|
|
)
|
|
mock_client.climate_command.assert_has_calls([call(key=1, custom_fan_mode="fan2")])
|
|
mock_client.climate_command.reset_mock()
|
|
|
|
await hass.services.async_call(
|
|
CLIMATE_DOMAIN,
|
|
SERVICE_SET_SWING_MODE,
|
|
{ATTR_ENTITY_ID: "climate.test_myclimate", ATTR_SWING_MODE: SWING_BOTH},
|
|
blocking=True,
|
|
)
|
|
mock_client.climate_command.assert_has_calls(
|
|
[call(key=1, swing_mode=ClimateSwingMode.BOTH)]
|
|
)
|
|
mock_client.climate_command.reset_mock()
|
|
|
|
|
|
async def test_climate_entity_with_humidity(
|
|
hass: HomeAssistant, mock_client: APIClient, mock_generic_device_entry
|
|
) -> None:
|
|
"""Test a generic climate entity with humidity."""
|
|
entity_info = [
|
|
ClimateInfo(
|
|
object_id="myclimate",
|
|
key=1,
|
|
name="my climate",
|
|
unique_id="my_climate",
|
|
supports_current_temperature=True,
|
|
supports_two_point_target_temperature=True,
|
|
supports_action=True,
|
|
visual_min_temperature=10.0,
|
|
visual_max_temperature=30.0,
|
|
supports_current_humidity=True,
|
|
supports_target_humidity=True,
|
|
visual_min_humidity=10.1,
|
|
visual_max_humidity=29.7,
|
|
)
|
|
]
|
|
states = [
|
|
ClimateState(
|
|
key=1,
|
|
mode=ClimateMode.AUTO,
|
|
action=ClimateAction.COOLING,
|
|
current_temperature=30,
|
|
target_temperature=20,
|
|
fan_mode=ClimateFanMode.AUTO,
|
|
swing_mode=ClimateSwingMode.BOTH,
|
|
current_humidity=20.1,
|
|
target_humidity=25.7,
|
|
)
|
|
]
|
|
user_service = []
|
|
await mock_generic_device_entry(
|
|
mock_client=mock_client,
|
|
entity_info=entity_info,
|
|
user_service=user_service,
|
|
states=states,
|
|
)
|
|
state = hass.states.get("climate.test_myclimate")
|
|
assert state is not None
|
|
assert state.state == HVACMode.AUTO
|
|
attributes = state.attributes
|
|
assert attributes[ATTR_CURRENT_HUMIDITY] == 20
|
|
assert attributes[ATTR_HUMIDITY] == 26
|
|
assert attributes[ATTR_MAX_HUMIDITY] == 30
|
|
assert attributes[ATTR_MIN_HUMIDITY] == 10
|
|
|
|
await hass.services.async_call(
|
|
CLIMATE_DOMAIN,
|
|
SERVICE_SET_HUMIDITY,
|
|
{ATTR_ENTITY_ID: "climate.test_myclimate", ATTR_HUMIDITY: 23},
|
|
blocking=True,
|
|
)
|
|
mock_client.climate_command.assert_has_calls([call(key=1, target_humidity=23)])
|
|
mock_client.climate_command.reset_mock()
|
|
|
|
|
|
async def test_climate_entity_with_inf_value(
|
|
hass: HomeAssistant, mock_client: APIClient, mock_generic_device_entry
|
|
) -> None:
|
|
"""Test a generic climate entity with infinite temp."""
|
|
entity_info = [
|
|
ClimateInfo(
|
|
object_id="myclimate",
|
|
key=1,
|
|
name="my climate",
|
|
unique_id="my_climate",
|
|
supports_current_temperature=True,
|
|
supports_two_point_target_temperature=True,
|
|
supports_action=True,
|
|
visual_min_temperature=10.0,
|
|
visual_max_temperature=30.0,
|
|
supports_current_humidity=True,
|
|
supports_target_humidity=True,
|
|
visual_min_humidity=10.1,
|
|
visual_max_humidity=29.7,
|
|
)
|
|
]
|
|
states = [
|
|
ClimateState(
|
|
key=1,
|
|
mode=ClimateMode.AUTO,
|
|
action=ClimateAction.COOLING,
|
|
current_temperature=math.inf,
|
|
target_temperature=math.inf,
|
|
fan_mode=ClimateFanMode.AUTO,
|
|
swing_mode=ClimateSwingMode.BOTH,
|
|
current_humidity=20.1,
|
|
target_humidity=25.7,
|
|
)
|
|
]
|
|
user_service = []
|
|
await mock_generic_device_entry(
|
|
mock_client=mock_client,
|
|
entity_info=entity_info,
|
|
user_service=user_service,
|
|
states=states,
|
|
)
|
|
state = hass.states.get("climate.test_myclimate")
|
|
assert state is not None
|
|
assert state.state == HVACMode.AUTO
|
|
attributes = state.attributes
|
|
assert attributes[ATTR_CURRENT_HUMIDITY] == 20
|
|
assert attributes[ATTR_HUMIDITY] == 26
|
|
assert attributes[ATTR_MAX_HUMIDITY] == 30
|
|
assert attributes[ATTR_MIN_HUMIDITY] == 10
|
|
assert ATTR_TEMPERATURE not in attributes
|
|
assert attributes[ATTR_CURRENT_TEMPERATURE] is None
|
|
|
|
|
|
async def test_climate_entity_attributes(
|
|
hass: HomeAssistant,
|
|
mock_client: APIClient,
|
|
mock_generic_device_entry,
|
|
snapshot: SnapshotAssertion,
|
|
) -> None:
|
|
"""Test a climate entity sets correct attributes."""
|
|
entity_info = [
|
|
ClimateInfo(
|
|
object_id="myclimate",
|
|
key=1,
|
|
name="my climate",
|
|
unique_id="my_climate",
|
|
supports_current_temperature=True,
|
|
visual_target_temperature_step=2,
|
|
visual_current_temperature_step=2,
|
|
supports_action=True,
|
|
visual_min_temperature=10.0,
|
|
visual_max_temperature=30.0,
|
|
supported_fan_modes=[ClimateFanMode.LOW, ClimateFanMode.HIGH],
|
|
supported_modes=[
|
|
ClimateMode.COOL,
|
|
ClimateMode.HEAT,
|
|
ClimateMode.AUTO,
|
|
ClimateMode.OFF,
|
|
],
|
|
supported_presets=[ClimatePreset.AWAY, ClimatePreset.ACTIVITY],
|
|
supported_custom_presets=["preset1", "preset2"],
|
|
supported_custom_fan_modes=["fan1", "fan2"],
|
|
supported_swing_modes=[ClimateSwingMode.BOTH, ClimateSwingMode.OFF],
|
|
)
|
|
]
|
|
states = [
|
|
ClimateState(
|
|
key=1,
|
|
mode=ClimateMode.COOL,
|
|
action=ClimateAction.COOLING,
|
|
current_temperature=30,
|
|
target_temperature=20,
|
|
fan_mode=ClimateFanMode.AUTO,
|
|
swing_mode=ClimateSwingMode.BOTH,
|
|
)
|
|
]
|
|
user_service = []
|
|
await mock_generic_device_entry(
|
|
mock_client=mock_client,
|
|
entity_info=entity_info,
|
|
user_service=user_service,
|
|
states=states,
|
|
)
|
|
state = hass.states.get("climate.test_myclimate")
|
|
assert state is not None
|
|
assert state.state == HVACMode.COOL
|
|
assert state.attributes == snapshot(name="climate-entity-attributes")
|