core/tests/components/teslemetry/test_climate.py

366 lines
11 KiB
Python
Raw Normal View History

"""Test the Teslemetry climate platform."""
from unittest.mock import AsyncMock, patch
from freezegun.api import FrozenDateTimeFactory
import pytest
from syrupy.assertion import SnapshotAssertion
from tesla_fleet_api.exceptions import InvalidCommand
from homeassistant.components.climate import (
ATTR_HVAC_MODE,
ATTR_PRESET_MODE,
ATTR_TEMPERATURE,
DOMAIN as CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
SERVICE_SET_PRESET_MODE,
SERVICE_SET_TEMPERATURE,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
HVACMode,
)
from homeassistant.const import ATTR_ENTITY_ID, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import entity_registry as er
from . import assert_entities, setup_platform
from .const import (
COMMAND_ERRORS,
COMMAND_IGNORED_REASON,
METADATA_NOSCOPE,
VEHICLE_DATA_ALT,
VEHICLE_DATA_ASLEEP,
WAKE_UP_ASLEEP,
WAKE_UP_ONLINE,
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_climate(
hass: HomeAssistant,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
) -> None:
"""Tests that the climate entity is correct."""
entry = await setup_platform(hass, [Platform.CLIMATE])
assert_entities(hass, entry.entry_id, entity_registry, snapshot)
entity_id = "climate.test_climate"
# Turn On and Set Temp
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{
ATTR_ENTITY_ID: [entity_id],
ATTR_TEMPERATURE: 20,
ATTR_HVAC_MODE: HVACMode.HEAT_COOL,
},
blocking=True,
)
state = hass.states.get(entity_id)
assert state.attributes[ATTR_TEMPERATURE] == 20
assert state.state == HVACMode.HEAT_COOL
# Set Temp
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{
ATTR_ENTITY_ID: [entity_id],
ATTR_TEMPERATURE: 21,
},
blocking=True,
)
state = hass.states.get(entity_id)
assert state.attributes[ATTR_TEMPERATURE] == 21
# Set Preset
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_PRESET_MODE,
{ATTR_ENTITY_ID: [entity_id], ATTR_PRESET_MODE: "keep"},
blocking=True,
)
state = hass.states.get(entity_id)
assert state.attributes[ATTR_PRESET_MODE] == "keep"
# Set Preset
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_PRESET_MODE,
{ATTR_ENTITY_ID: [entity_id], ATTR_PRESET_MODE: "off"},
blocking=True,
)
state = hass.states.get(entity_id)
assert state.attributes[ATTR_PRESET_MODE] == "off"
# Turn Off
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
{ATTR_ENTITY_ID: [entity_id], ATTR_HVAC_MODE: HVACMode.OFF},
blocking=True,
)
state = hass.states.get(entity_id)
assert state.state == HVACMode.OFF
entity_id = "climate.test_cabin_overheat_protection"
# Turn On and Set Low
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{
ATTR_ENTITY_ID: [entity_id],
ATTR_TEMPERATURE: 30,
ATTR_HVAC_MODE: HVACMode.FAN_ONLY,
},
blocking=True,
)
state = hass.states.get(entity_id)
assert state.attributes[ATTR_TEMPERATURE] == 30
assert state.state == HVACMode.FAN_ONLY
# Set Temp Medium
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{
ATTR_ENTITY_ID: [entity_id],
ATTR_TEMPERATURE: 35,
},
blocking=True,
)
state = hass.states.get(entity_id)
assert state.attributes[ATTR_TEMPERATURE] == 35
# Set Temp High
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{
ATTR_ENTITY_ID: [entity_id],
ATTR_TEMPERATURE: 40,
},
blocking=True,
)
state = hass.states.get(entity_id)
assert state.attributes[ATTR_TEMPERATURE] == 40
# Turn Off
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: [entity_id]},
blocking=True,
)
state = hass.states.get(entity_id)
assert state.state == HVACMode.OFF
# Turn On
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: [entity_id]},
blocking=True,
)
state = hass.states.get(entity_id)
assert state.state == HVACMode.COOL
state = hass.states.get(entity_id)
assert state.attributes[ATTR_TEMPERATURE] == 40
assert state.state == HVACMode.COOL
# pytest raises ServiceValidationError
with pytest.raises(
ServiceValidationError,
match="Cabin overheat protection does not support that temperature",
):
# Invalid Temp
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{ATTR_ENTITY_ID: [entity_id], ATTR_TEMPERATURE: 34},
blocking=True,
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_climate_alt(
hass: HomeAssistant,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
mock_vehicle_data: AsyncMock,
) -> None:
"""Tests that the climate entity is correct."""
mock_vehicle_data.return_value = VEHICLE_DATA_ALT
entry = await setup_platform(hass, [Platform.CLIMATE])
assert_entities(hass, entry.entry_id, entity_registry, snapshot)
async def test_invalid_error(hass: HomeAssistant, snapshot: SnapshotAssertion) -> None:
"""Tests service error is handled."""
await setup_platform(hass, platforms=[Platform.CLIMATE])
entity_id = "climate.test_climate"
with (
patch(
"homeassistant.components.teslemetry.VehicleSpecific.auto_conditioning_start",
side_effect=InvalidCommand,
) as mock_on,
pytest.raises(HomeAssistantError) as error,
):
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: [entity_id]},
blocking=True,
)
2024-06-08 15:59:08 +00:00
mock_on.assert_called_once()
assert str(error.value) == snapshot(name="error")
@pytest.mark.parametrize("response", COMMAND_ERRORS)
async def test_errors(hass: HomeAssistant, response: str) -> None:
"""Tests service reason is handled."""
await setup_platform(hass, platforms=[Platform.CLIMATE])
entity_id = "climate.test_climate"
with (
patch(
"homeassistant.components.teslemetry.VehicleSpecific.auto_conditioning_start",
return_value=response,
) as mock_on,
pytest.raises(HomeAssistantError),
):
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: [entity_id]},
blocking=True,
)
2024-06-08 15:59:08 +00:00
mock_on.assert_called_once()
async def test_ignored_error(
hass: HomeAssistant,
) -> None:
"""Tests ignored error is handled."""
await setup_platform(hass, [Platform.CLIMATE])
entity_id = "climate.test_climate"
with patch(
"homeassistant.components.teslemetry.VehicleSpecific.auto_conditioning_start",
return_value=COMMAND_IGNORED_REASON,
) as mock_on:
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: [entity_id]},
blocking=True,
)
mock_on.assert_called_once()
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_asleep_or_offline(
hass: HomeAssistant,
mock_vehicle_data: AsyncMock,
mock_wake_up: AsyncMock,
mock_vehicle: AsyncMock,
freezer: FrozenDateTimeFactory,
snapshot: SnapshotAssertion,
) -> None:
"""Tests asleep is handled."""
mock_vehicle_data.return_value = VEHICLE_DATA_ASLEEP
await setup_platform(hass, [Platform.CLIMATE])
entity_id = "climate.test_climate"
# Run a command but fail trying to wake up the vehicle
mock_wake_up.side_effect = InvalidCommand
with pytest.raises(HomeAssistantError) as error:
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: [entity_id]},
blocking=True,
)
assert str(error.value) == snapshot(name="InvalidCommand")
mock_wake_up.assert_called_once()
mock_wake_up.side_effect = None
mock_wake_up.reset_mock()
# Run a command but timeout trying to wake up the vehicle
mock_wake_up.return_value = WAKE_UP_ASLEEP
mock_vehicle.return_value = WAKE_UP_ASLEEP
with (
patch("homeassistant.components.teslemetry.helpers.asyncio.sleep"),
pytest.raises(HomeAssistantError) as error,
):
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: [entity_id]},
blocking=True,
)
assert str(error.value) == snapshot(name="HomeAssistantError")
mock_wake_up.assert_called_once()
mock_vehicle.assert_called()
mock_wake_up.reset_mock()
mock_vehicle.reset_mock()
mock_wake_up.return_value = WAKE_UP_ONLINE
mock_vehicle.return_value = WAKE_UP_ONLINE
# Run a command and wake up the vehicle immediately
await hass.services.async_call(
CLIMATE_DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: [entity_id]}, blocking=True
)
await hass.async_block_till_done()
mock_wake_up.assert_called_once()
async def test_climate_noscope(
hass: HomeAssistant,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
mock_metadata: AsyncMock,
) -> None:
"""Tests that the climate entity is correct."""
mock_metadata.return_value = METADATA_NOSCOPE
entry = await setup_platform(hass, [Platform.CLIMATE])
entity_entries = er.async_entries_for_config_entry(entity_registry, entry.entry_id)
assert entity_entries
for entity_entry in entity_entries:
assert entity_entry == snapshot(name=f"{entity_entry.entity_id}-entry")
entity_id = "climate.test_climate"
with pytest.raises(ServiceValidationError):
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
{ATTR_ENTITY_ID: [entity_id], ATTR_HVAC_MODE: HVACMode.HEAT_COOL},
blocking=True,
)
with pytest.raises(HomeAssistantError):
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{ATTR_ENTITY_ID: [entity_id], ATTR_TEMPERATURE: 20},
blocking=True,
)