1
0
mirror of https://github.com/home-assistant/core.git synced 2026-06-02 21:54:27 +01:00
Files
core/tests/components/evohome/test_climate.py
T

484 lines
14 KiB
Python

"""The tests for the climate platform of evohome.
All evohome systems have controllers and at least one zone.
"""
from datetime import timedelta
from unittest.mock import patch
from evohomeasync2 import exceptions as evo_exc
from freezegun.api import FrozenDateTimeFactory
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.components.climate import (
ATTR_HVAC_MODE,
ATTR_PRESET_MODE,
DOMAIN as CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
SERVICE_SET_PRESET_MODE,
SERVICE_SET_TEMPERATURE,
HVACMode,
)
from homeassistant.components.evohome.climate import PRESET_RESET
from homeassistant.components.evohome.const import DOMAIN, RESET_BREAKS_IN_HA_VERSION
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_TEMPERATURE,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import issue_registry as ir
from .conftest import setup_evohome
from .const import TEST_INSTALLS
from tests.common import async_fire_time_changed
@pytest.mark.parametrize("install", [*TEST_INSTALLS, "botched"])
async def test_setup_platform(
hass: HomeAssistant,
config: dict[str, str],
install: str,
snapshot: SnapshotAssertion,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test entities and their states after setup of evohome."""
# Cannot use the evohome fixture here, as need to set dtm first
# - some extended state attrs are relative the current time
freezer.move_to("2024-07-10T12:00:00Z")
async for _ in setup_evohome(hass, config, install=install):
pass
climate_states = hass.states.async_all(CLIMATE_DOMAIN)
assert climate_states
for x in climate_states:
assert x == snapshot(name=f"{x.entity_id}-state")
@pytest.mark.parametrize("install", ["default"])
async def test_entities_update_over_time(
hass: HomeAssistant,
config: dict[str, str],
install: str,
snapshot: SnapshotAssertion,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test extended attributes update as time passes.
Verifies that time-dependent state attrs (e.g. schedules) vary as time advances.
"""
# Cannot use the evohome fixture here, as need to set dtm first
# - some extended state attrs are relative the current time
freezer.move_to("2024-07-10T05:30:00Z")
# stay inside this context to have the mocked RESTful API
async for _ in setup_evohome(hass, config, install=install):
for x in hass.states.async_all(CLIMATE_DOMAIN):
assert x == snapshot(name=f"{x.entity_id}-state-initial")
freezer.tick(timedelta(hours=12))
async_fire_time_changed(hass)
await hass.async_block_till_done(wait_background_tasks=True)
for x in hass.states.async_all(CLIMATE_DOMAIN):
assert x == snapshot(name=f"{x.entity_id}-state-updated")
@pytest.mark.parametrize("install", TEST_INSTALLS)
async def test_ctl_set_hvac_mode(
hass: HomeAssistant,
ctl_id: str,
snapshot: SnapshotAssertion,
) -> None:
"""Test SERVICE_SET_HVAC_MODE of an evohome controller."""
results = []
# SERVICE_SET_HVAC_MODE: HVACMode.OFF
with patch("evohomeasync2.control_system.ControlSystem.set_mode") as mock_fcn:
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
{
ATTR_ENTITY_ID: ctl_id,
ATTR_HVAC_MODE: HVACMode.OFF,
},
blocking=True,
)
try:
mock_fcn.assert_awaited_once_with("HeatingOff", until=None)
except AssertionError:
mock_fcn.assert_awaited_once_with("Off", until=None)
results.append(mock_fcn.await_args.args) # type: ignore[union-attr]
# SERVICE_SET_HVAC_MODE: HVACMode.HEAT
with patch("evohomeasync2.control_system.ControlSystem.set_mode") as mock_fcn:
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
{
ATTR_ENTITY_ID: ctl_id,
ATTR_HVAC_MODE: HVACMode.HEAT,
},
blocking=True,
)
try:
mock_fcn.assert_awaited_once_with("Auto", until=None)
except AssertionError:
mock_fcn.assert_awaited_once_with("Heat", until=None)
results.append(mock_fcn.await_args.args) # type: ignore[union-attr]
assert results == snapshot
@pytest.mark.parametrize("install", TEST_INSTALLS)
async def test_ctl_set_temperature(
hass: HomeAssistant,
ctl_id: str,
) -> None:
"""Test SERVICE_SET_TEMPERATURE of an evohome controller."""
# Entity climate.xxx does not support this service
with pytest.raises(HomeAssistantError):
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{
ATTR_ENTITY_ID: ctl_id,
ATTR_TEMPERATURE: 19.1,
},
blocking=True,
)
@pytest.mark.parametrize("install", TEST_INSTALLS)
async def test_ctl_turn_off(
hass: HomeAssistant,
ctl_id: str,
snapshot: SnapshotAssertion,
) -> None:
"""Test SERVICE_TURN_OFF of an evohome controller."""
results = []
# SERVICE_TURN_OFF
with patch("evohomeasync2.control_system.ControlSystem.set_mode") as mock_fcn:
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_TURN_OFF,
{
ATTR_ENTITY_ID: ctl_id,
},
blocking=True,
)
try:
mock_fcn.assert_awaited_once_with("HeatingOff", until=None)
except AssertionError:
mock_fcn.assert_awaited_once_with("Off", until=None)
results.append(mock_fcn.await_args.args) # type: ignore[union-attr]
assert results == snapshot
@pytest.mark.parametrize("install", ["default"])
async def test_ctl_invalid_system_mode(
hass: HomeAssistant,
ctl_id: str,
) -> None:
"""Test translated exception when the requested system mode is invalid."""
with (
patch(
"evohomeasync2.control_system.ControlSystem.set_mode",
side_effect=evo_exc.InvalidSystemModeError("Unsupported mode: xxx"),
),
pytest.raises(ServiceValidationError) as exc_info,
):
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_TURN_OFF,
{
ATTR_ENTITY_ID: ctl_id,
},
blocking=True,
)
assert exc_info.value.translation_key == "invalid_system_mode"
@pytest.mark.parametrize("install", TEST_INSTALLS)
async def test_ctl_turn_on(
hass: HomeAssistant,
ctl_id: str,
snapshot: SnapshotAssertion,
) -> None:
"""Test SERVICE_TURN_ON of an evohome controller."""
results = []
# SERVICE_TURN_ON
with patch("evohomeasync2.control_system.ControlSystem.set_mode") as mock_fcn:
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_TURN_ON,
{
ATTR_ENTITY_ID: ctl_id,
},
blocking=True,
)
try:
mock_fcn.assert_awaited_once_with("Auto", until=None)
except AssertionError:
mock_fcn.assert_awaited_once_with("Heat", until=None)
results.append(mock_fcn.await_args.args) # type: ignore[union-attr]
assert results == snapshot
@pytest.mark.parametrize("install", ["default"])
async def test_ctl_preset_reset_deprecated(
hass: HomeAssistant,
ctl_id: str,
issue_registry: ir.IssueRegistry,
) -> None:
"""Test deprecated warning when using the Reset preset on controllers."""
with patch("evohomeasync2.control_system.ControlSystem.set_mode") as mock_fcn:
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_PRESET_MODE,
{
ATTR_ENTITY_ID: ctl_id,
ATTR_PRESET_MODE: PRESET_RESET,
},
blocking=True,
)
mock_fcn.assert_awaited_once_with("AutoWithReset", until=None)
issue = issue_registry.async_get_issue(DOMAIN, "deprecated_preset_reset")
assert issue is not None
assert issue.translation_key == "deprecated_preset_reset"
assert issue.translation_placeholders == {
"breaks_in_ha_version": RESET_BREAKS_IN_HA_VERSION,
}
@pytest.mark.parametrize("install", TEST_INSTALLS)
async def test_zone_set_hvac_mode(
hass: HomeAssistant,
zone_id: str,
snapshot: SnapshotAssertion,
) -> None:
"""Test SERVICE_SET_HVAC_MODE of an evohome heating zone."""
results = []
# SERVICE_SET_HVAC_MODE: HVACMode.HEAT
with patch("evohomeasync2.zone.Zone.reset") as mock_fcn:
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
{
ATTR_ENTITY_ID: zone_id,
ATTR_HVAC_MODE: HVACMode.HEAT,
},
blocking=True,
)
mock_fcn.assert_awaited_once_with()
# SERVICE_SET_HVAC_MODE: HVACMode.OFF
with patch("evohomeasync2.zone.Zone.set_temperature") as mock_fcn:
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
{
ATTR_ENTITY_ID: zone_id,
ATTR_HVAC_MODE: HVACMode.OFF,
},
blocking=True,
)
mock_fcn.assert_awaited_once()
assert mock_fcn.await_args is not None # mypy hint
assert mock_fcn.await_args.args != () # minimum target temp
assert mock_fcn.await_args.kwargs == {"until": None}
results.append(mock_fcn.await_args.args)
assert results == snapshot
@pytest.mark.parametrize("install", TEST_INSTALLS)
async def test_zone_set_preset_mode(
hass: HomeAssistant,
zone_id: str,
freezer: FrozenDateTimeFactory,
snapshot: SnapshotAssertion,
) -> None:
"""Test SERVICE_SET_PRESET_MODE of an evohome heating zone."""
freezer.move_to("2024-07-10T12:00:00Z")
results = []
# SERVICE_SET_PRESET_MODE: none
with patch("evohomeasync2.zone.Zone.reset") as mock_fcn:
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_PRESET_MODE,
{
ATTR_ENTITY_ID: zone_id,
ATTR_PRESET_MODE: "none",
},
blocking=True,
)
mock_fcn.assert_awaited_once_with()
# SERVICE_SET_PRESET_MODE: permanent
with patch("evohomeasync2.zone.Zone.set_temperature") as mock_fcn:
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_PRESET_MODE,
{
ATTR_ENTITY_ID: zone_id,
ATTR_PRESET_MODE: "permanent",
},
blocking=True,
)
mock_fcn.assert_awaited_once()
assert mock_fcn.await_args is not None # mypy hint
assert mock_fcn.await_args.args != () # current target temp
assert mock_fcn.await_args.kwargs == {"until": None}
results.append(mock_fcn.await_args.args)
# SERVICE_SET_PRESET_MODE: temporary
with patch("evohomeasync2.zone.Zone.set_temperature") as mock_fcn:
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_PRESET_MODE,
{
ATTR_ENTITY_ID: zone_id,
ATTR_PRESET_MODE: "temporary",
},
blocking=True,
)
mock_fcn.assert_awaited_once()
assert mock_fcn.await_args is not None # mypy hint
assert mock_fcn.await_args.args != () # current target temp
assert mock_fcn.await_args.kwargs != {} # next setpoint dtm
results.append(mock_fcn.await_args.args)
results.append(mock_fcn.await_args.kwargs)
assert results == snapshot
@pytest.mark.parametrize("install", TEST_INSTALLS)
async def test_zone_set_temperature(
hass: HomeAssistant,
zone_id: str,
snapshot: SnapshotAssertion,
) -> None:
"""Test SERVICE_SET_TEMPERATURE of an evohome heating zone."""
results = []
# SERVICE_SET_TEMPERATURE: temperature
with patch("evohomeasync2.zone.Zone.set_temperature") as mock_fcn:
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{
ATTR_ENTITY_ID: zone_id,
ATTR_TEMPERATURE: 19.1,
},
blocking=True,
)
mock_fcn.assert_awaited_once()
assert mock_fcn.await_args is not None # mypy hint
assert mock_fcn.await_args.args == (19.1,)
assert mock_fcn.await_args.kwargs != {} # next setpoint dtm
results.append(mock_fcn.await_args.kwargs)
assert results == snapshot
@pytest.mark.parametrize("install", TEST_INSTALLS)
async def test_zone_turn_off(
hass: HomeAssistant,
zone_id: str,
snapshot: SnapshotAssertion,
) -> None:
"""Test SERVICE_TURN_OFF of an evohome heating zone."""
results = []
# SERVICE_TURN_OFF
with patch("evohomeasync2.zone.Zone.set_temperature") as mock_fcn:
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_TURN_OFF,
{
ATTR_ENTITY_ID: zone_id,
},
blocking=True,
)
mock_fcn.assert_awaited_once()
assert mock_fcn.await_args is not None # mypy hint
assert mock_fcn.await_args.args != () # minimum target temp
assert mock_fcn.await_args.kwargs == {"until": None}
results.append(mock_fcn.await_args.args)
assert results == snapshot
@pytest.mark.parametrize("install", TEST_INSTALLS)
async def test_zone_turn_on(
hass: HomeAssistant,
zone_id: str,
) -> None:
"""Test SERVICE_TURN_ON of an evohome heating zone."""
# SERVICE_TURN_ON
with patch("evohomeasync2.zone.Zone.reset") as mock_fcn:
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_TURN_ON,
{
ATTR_ENTITY_ID: zone_id,
},
blocking=True,
)
mock_fcn.assert_awaited_once_with()