mirror of https://github.com/home-assistant/core
1621 lines
54 KiB
Python
1621 lines
54 KiB
Python
"""Test for Nest climate platform for the Smart Device Management API.
|
|
|
|
These tests fake out the subscriber/devicemanager, and are not using a real
|
|
pubsub subscriber.
|
|
"""
|
|
|
|
from collections.abc import Awaitable, Callable
|
|
from http import HTTPStatus
|
|
from typing import Any
|
|
|
|
import aiohttp
|
|
from google_nest_sdm.auth import AbstractAuth
|
|
from google_nest_sdm.event import EventMessage
|
|
import pytest
|
|
|
|
from homeassistant.components.climate import (
|
|
ATTR_CURRENT_TEMPERATURE,
|
|
ATTR_FAN_MODE,
|
|
ATTR_FAN_MODES,
|
|
ATTR_HVAC_ACTION,
|
|
ATTR_HVAC_MODES,
|
|
ATTR_PRESET_MODE,
|
|
ATTR_PRESET_MODES,
|
|
ATTR_TARGET_TEMP_HIGH,
|
|
ATTR_TARGET_TEMP_LOW,
|
|
FAN_LOW,
|
|
FAN_OFF,
|
|
FAN_ON,
|
|
PRESET_ECO,
|
|
PRESET_NONE,
|
|
PRESET_SLEEP,
|
|
ClimateEntityFeature,
|
|
HVACAction,
|
|
HVACMode,
|
|
)
|
|
from homeassistant.const import (
|
|
ATTR_SUPPORTED_FEATURES,
|
|
ATTR_TEMPERATURE,
|
|
STATE_UNAVAILABLE,
|
|
)
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
|
|
|
from .common import (
|
|
DEVICE_COMMAND,
|
|
DEVICE_ID,
|
|
CreateDevice,
|
|
FakeSubscriber,
|
|
PlatformSetup,
|
|
)
|
|
from .conftest import FakeAuth
|
|
|
|
from tests.components.climate import common
|
|
|
|
type CreateEvent = Callable[[dict[str, Any]], Awaitable[None]]
|
|
|
|
EVENT_ID = "some-event-id"
|
|
|
|
|
|
@pytest.fixture
|
|
def platforms() -> list[str]:
|
|
"""Fixture to specify platforms to test."""
|
|
return ["climate"]
|
|
|
|
|
|
@pytest.fixture
|
|
def device_traits() -> dict[str, Any]:
|
|
"""Fixture that sets default traits used for devices."""
|
|
return {"sdm.devices.traits.Info": {"customName": "My Thermostat"}}
|
|
|
|
|
|
@pytest.fixture
|
|
async def create_event(
|
|
hass: HomeAssistant,
|
|
auth: AbstractAuth,
|
|
subscriber: FakeSubscriber,
|
|
) -> CreateEvent:
|
|
"""Fixture to send a pub/sub event."""
|
|
|
|
async def create_event(traits: dict[str, Any]) -> None:
|
|
await subscriber.async_receive_event(
|
|
EventMessage.create_event(
|
|
{
|
|
"eventId": EVENT_ID,
|
|
"timestamp": "2019-01-01T00:00:01Z",
|
|
"resourceUpdate": {
|
|
"name": DEVICE_ID,
|
|
"traits": traits,
|
|
},
|
|
},
|
|
auth=auth,
|
|
)
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
return create_event
|
|
|
|
|
|
async def test_no_devices(hass: HomeAssistant, setup_platform: PlatformSetup) -> None:
|
|
"""Test no devices returned by the api."""
|
|
await setup_platform()
|
|
assert len(hass.states.async_all()) == 0
|
|
|
|
|
|
async def test_climate_devices(
|
|
hass: HomeAssistant, setup_platform: PlatformSetup, create_device: CreateDevice
|
|
) -> None:
|
|
"""Test no eligible climate devices returned by the api."""
|
|
create_device.create({"sdm.devices.traits.CameraImage": {}})
|
|
await setup_platform()
|
|
assert len(hass.states.async_all()) == 0
|
|
|
|
|
|
async def test_thermostat_off(
|
|
hass: HomeAssistant, setup_platform: PlatformSetup, create_device: CreateDevice
|
|
) -> None:
|
|
"""Test a thermostat that is not running."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.ThermostatHvac": {"status": "OFF"},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "OFF",
|
|
},
|
|
"sdm.devices.traits.Temperature": {
|
|
"ambientTemperatureCelsius": 16.2,
|
|
},
|
|
},
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.OFF
|
|
assert thermostat.attributes[ATTR_HVAC_ACTION] == HVACAction.OFF
|
|
assert thermostat.attributes[ATTR_CURRENT_TEMPERATURE] == 16.2
|
|
assert set(thermostat.attributes[ATTR_HVAC_MODES]) == {
|
|
HVACMode.HEAT,
|
|
HVACMode.COOL,
|
|
HVACMode.HEAT_COOL,
|
|
HVACMode.OFF,
|
|
}
|
|
assert thermostat.attributes[ATTR_TEMPERATURE] is None
|
|
assert thermostat.attributes[ATTR_TARGET_TEMP_LOW] is None
|
|
assert thermostat.attributes[ATTR_TARGET_TEMP_HIGH] is None
|
|
assert ATTR_PRESET_MODE not in thermostat.attributes
|
|
assert ATTR_PRESET_MODES not in thermostat.attributes
|
|
assert ATTR_FAN_MODE not in thermostat.attributes
|
|
assert ATTR_FAN_MODES not in thermostat.attributes
|
|
|
|
|
|
async def test_thermostat_heat(
|
|
hass: HomeAssistant, setup_platform: PlatformSetup, create_device: CreateDevice
|
|
) -> None:
|
|
"""Test a thermostat that is heating."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.ThermostatHvac": {
|
|
"status": "HEATING",
|
|
},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "HEAT",
|
|
},
|
|
"sdm.devices.traits.Temperature": {
|
|
"ambientTemperatureCelsius": 16.2,
|
|
},
|
|
"sdm.devices.traits.ThermostatTemperatureSetpoint": {
|
|
"heatCelsius": 22.0,
|
|
},
|
|
},
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.HEAT
|
|
assert thermostat.attributes[ATTR_HVAC_ACTION] == HVACAction.HEATING
|
|
assert thermostat.attributes[ATTR_CURRENT_TEMPERATURE] == 16.2
|
|
assert set(thermostat.attributes[ATTR_HVAC_MODES]) == {
|
|
HVACMode.HEAT,
|
|
HVACMode.COOL,
|
|
HVACMode.HEAT_COOL,
|
|
HVACMode.OFF,
|
|
}
|
|
assert thermostat.attributes[ATTR_TEMPERATURE] == 22.0
|
|
assert thermostat.attributes[ATTR_TARGET_TEMP_LOW] is None
|
|
assert thermostat.attributes[ATTR_TARGET_TEMP_HIGH] is None
|
|
assert ATTR_PRESET_MODE not in thermostat.attributes
|
|
assert ATTR_PRESET_MODES not in thermostat.attributes
|
|
|
|
|
|
async def test_thermostat_cool(
|
|
hass: HomeAssistant, setup_platform: PlatformSetup, create_device: CreateDevice
|
|
) -> None:
|
|
"""Test a thermostat that is cooling."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.ThermostatHvac": {
|
|
"status": "COOLING",
|
|
},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "COOL",
|
|
},
|
|
"sdm.devices.traits.Temperature": {
|
|
"ambientTemperatureCelsius": 29.9,
|
|
},
|
|
"sdm.devices.traits.ThermostatTemperatureSetpoint": {
|
|
"coolCelsius": 28.0,
|
|
},
|
|
},
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.COOL
|
|
assert thermostat.attributes[ATTR_HVAC_ACTION] == HVACAction.COOLING
|
|
assert thermostat.attributes[ATTR_CURRENT_TEMPERATURE] == 29.9
|
|
assert set(thermostat.attributes[ATTR_HVAC_MODES]) == {
|
|
HVACMode.HEAT,
|
|
HVACMode.COOL,
|
|
HVACMode.HEAT_COOL,
|
|
HVACMode.OFF,
|
|
}
|
|
assert thermostat.attributes[ATTR_TEMPERATURE] == 28.0
|
|
assert thermostat.attributes[ATTR_TARGET_TEMP_LOW] is None
|
|
assert thermostat.attributes[ATTR_TARGET_TEMP_HIGH] is None
|
|
assert ATTR_PRESET_MODE not in thermostat.attributes
|
|
assert ATTR_PRESET_MODES not in thermostat.attributes
|
|
|
|
|
|
async def test_thermostat_heatcool(
|
|
hass: HomeAssistant, setup_platform: PlatformSetup, create_device: CreateDevice
|
|
) -> None:
|
|
"""Test a thermostat that is cooling in heatcool mode."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.ThermostatHvac": {
|
|
"status": "COOLING",
|
|
},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "HEATCOOL",
|
|
},
|
|
"sdm.devices.traits.Temperature": {
|
|
"ambientTemperatureCelsius": 29.9,
|
|
},
|
|
"sdm.devices.traits.ThermostatTemperatureSetpoint": {
|
|
"heatCelsius": 22.0,
|
|
"coolCelsius": 28.0,
|
|
},
|
|
},
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.HEAT_COOL
|
|
assert thermostat.attributes[ATTR_HVAC_ACTION] == HVACAction.COOLING
|
|
assert thermostat.attributes[ATTR_CURRENT_TEMPERATURE] == 29.9
|
|
assert set(thermostat.attributes[ATTR_HVAC_MODES]) == {
|
|
HVACMode.HEAT,
|
|
HVACMode.COOL,
|
|
HVACMode.HEAT_COOL,
|
|
HVACMode.OFF,
|
|
}
|
|
assert thermostat.attributes[ATTR_TARGET_TEMP_LOW] == 22.0
|
|
assert thermostat.attributes[ATTR_TARGET_TEMP_HIGH] == 28.0
|
|
assert thermostat.attributes[ATTR_TEMPERATURE] is None
|
|
assert ATTR_PRESET_MODE not in thermostat.attributes
|
|
assert ATTR_PRESET_MODES not in thermostat.attributes
|
|
|
|
|
|
async def test_thermostat_eco_off(
|
|
hass: HomeAssistant, setup_platform: PlatformSetup, create_device: CreateDevice
|
|
) -> None:
|
|
"""Test a thermostat cooling with eco off."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.ThermostatHvac": {
|
|
"status": "COOLING",
|
|
},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "HEATCOOL",
|
|
},
|
|
"sdm.devices.traits.ThermostatEco": {
|
|
"availableModes": ["MANUAL_ECO", "OFF"],
|
|
"mode": "OFF",
|
|
"heatCelsius": 20.0,
|
|
"coolCelsius": 22.0,
|
|
},
|
|
"sdm.devices.traits.Temperature": {
|
|
"ambientTemperatureCelsius": 29.9,
|
|
},
|
|
"sdm.devices.traits.ThermostatTemperatureSetpoint": {
|
|
"heatCelsius": 22.0,
|
|
"coolCelsius": 28.0,
|
|
},
|
|
},
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.HEAT_COOL
|
|
assert thermostat.attributes[ATTR_HVAC_ACTION] == HVACAction.COOLING
|
|
assert thermostat.attributes[ATTR_CURRENT_TEMPERATURE] == 29.9
|
|
assert set(thermostat.attributes[ATTR_HVAC_MODES]) == {
|
|
HVACMode.HEAT,
|
|
HVACMode.COOL,
|
|
HVACMode.HEAT_COOL,
|
|
HVACMode.OFF,
|
|
}
|
|
assert thermostat.attributes[ATTR_TARGET_TEMP_LOW] == 22.0
|
|
assert thermostat.attributes[ATTR_TARGET_TEMP_HIGH] == 28.0
|
|
assert thermostat.attributes[ATTR_TEMPERATURE] is None
|
|
assert thermostat.attributes[ATTR_PRESET_MODE] == PRESET_NONE
|
|
assert thermostat.attributes[ATTR_PRESET_MODES] == [PRESET_ECO, PRESET_NONE]
|
|
|
|
|
|
async def test_thermostat_eco_on(
|
|
hass: HomeAssistant, setup_platform: PlatformSetup, create_device: CreateDevice
|
|
) -> None:
|
|
"""Test a thermostat in eco mode."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.ThermostatHvac": {
|
|
"status": "COOLING",
|
|
},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "HEATCOOL",
|
|
},
|
|
"sdm.devices.traits.ThermostatEco": {
|
|
"availableModes": ["MANUAL_ECO", "OFF"],
|
|
"mode": "MANUAL_ECO",
|
|
"heatCelsius": 21.0,
|
|
"coolCelsius": 29.0,
|
|
},
|
|
"sdm.devices.traits.Temperature": {
|
|
"ambientTemperatureCelsius": 29.9,
|
|
},
|
|
"sdm.devices.traits.ThermostatTemperatureSetpoint": {
|
|
"heatCelsius": 22.0,
|
|
"coolCelsius": 28.0,
|
|
},
|
|
},
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.HEAT_COOL
|
|
assert thermostat.attributes[ATTR_HVAC_ACTION] == HVACAction.COOLING
|
|
assert thermostat.attributes[ATTR_CURRENT_TEMPERATURE] == 29.9
|
|
assert set(thermostat.attributes[ATTR_HVAC_MODES]) == {
|
|
HVACMode.HEAT,
|
|
HVACMode.COOL,
|
|
HVACMode.HEAT_COOL,
|
|
HVACMode.OFF,
|
|
}
|
|
assert thermostat.attributes[ATTR_TARGET_TEMP_LOW] == 21.0
|
|
assert thermostat.attributes[ATTR_TARGET_TEMP_HIGH] == 29.0
|
|
assert thermostat.attributes[ATTR_TEMPERATURE] is None
|
|
assert thermostat.attributes[ATTR_PRESET_MODE] == PRESET_ECO
|
|
assert thermostat.attributes[ATTR_PRESET_MODES] == [PRESET_ECO, PRESET_NONE]
|
|
|
|
|
|
async def test_thermostat_eco_heat_only(
|
|
hass: HomeAssistant, setup_platform: PlatformSetup, create_device: CreateDevice
|
|
) -> None:
|
|
"""Test a thermostat in eco mode that only supports heat."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.ThermostatHvac": {
|
|
"status": "OFF",
|
|
},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "OFF"],
|
|
"mode": "HEAT",
|
|
},
|
|
"sdm.devices.traits.ThermostatEco": {
|
|
"availableModes": ["MANUAL_ECO", "OFF"],
|
|
"mode": "MANUAL_ECO",
|
|
"heatCelsius": 21.0,
|
|
"coolCelsius": 29.0,
|
|
},
|
|
"sdm.devices.traits.Temperature": {
|
|
"ambientTemperatureCelsius": 29.9,
|
|
},
|
|
"sdm.devices.traits.ThermostatTemperatureSetpoint": {},
|
|
},
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.HEAT
|
|
assert thermostat.attributes[ATTR_HVAC_ACTION] == HVACAction.IDLE
|
|
assert thermostat.attributes[ATTR_CURRENT_TEMPERATURE] == 29.9
|
|
assert set(thermostat.attributes[ATTR_HVAC_MODES]) == {
|
|
HVACMode.HEAT,
|
|
HVACMode.OFF,
|
|
}
|
|
assert thermostat.attributes[ATTR_TEMPERATURE] == 21.0
|
|
assert ATTR_TARGET_TEMP_LOW not in thermostat.attributes
|
|
assert ATTR_TARGET_TEMP_HIGH not in thermostat.attributes
|
|
assert thermostat.attributes[ATTR_PRESET_MODE] == PRESET_ECO
|
|
assert thermostat.attributes[ATTR_PRESET_MODES] == [PRESET_ECO, PRESET_NONE]
|
|
|
|
|
|
async def test_thermostat_set_hvac_mode(
|
|
hass: HomeAssistant,
|
|
setup_platform: PlatformSetup,
|
|
auth: FakeAuth,
|
|
create_device: CreateDevice,
|
|
create_event: CreateEvent,
|
|
) -> None:
|
|
"""Test a thermostat changing hvac modes."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.ThermostatHvac": {"status": "OFF"},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "OFF",
|
|
},
|
|
}
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.OFF
|
|
assert thermostat.attributes[ATTR_HVAC_ACTION] == HVACAction.OFF
|
|
|
|
await common.async_set_hvac_mode(hass, HVACMode.HEAT)
|
|
await hass.async_block_till_done()
|
|
|
|
assert auth.method == "post"
|
|
assert auth.url == DEVICE_COMMAND
|
|
assert auth.json == {
|
|
"command": "sdm.devices.commands.ThermostatMode.SetMode",
|
|
"params": {"mode": "HEAT"},
|
|
}
|
|
|
|
# Local state does not reflect the update
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.OFF
|
|
assert thermostat.attributes[ATTR_HVAC_ACTION] == HVACAction.OFF
|
|
|
|
# Simulate pubsub message when mode changes
|
|
await create_event(
|
|
{
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "HEAT",
|
|
},
|
|
}
|
|
)
|
|
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.HEAT
|
|
assert thermostat.attributes[ATTR_HVAC_ACTION] == HVACAction.IDLE
|
|
|
|
# Simulate pubsub message when the thermostat starts heating
|
|
await create_event(
|
|
{
|
|
"sdm.devices.traits.ThermostatHvac": {
|
|
"status": "HEATING",
|
|
},
|
|
}
|
|
)
|
|
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.HEAT
|
|
assert thermostat.attributes[ATTR_HVAC_ACTION] == HVACAction.HEATING
|
|
|
|
|
|
async def test_thermostat_invalid_hvac_mode(
|
|
hass: HomeAssistant,
|
|
setup_platform: PlatformSetup,
|
|
auth: FakeAuth,
|
|
create_device: CreateDevice,
|
|
) -> None:
|
|
"""Test setting an hvac_mode that is not supported."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.ThermostatHvac": {"status": "OFF"},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "OFF",
|
|
},
|
|
}
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.OFF
|
|
assert thermostat.attributes[ATTR_HVAC_ACTION] == HVACAction.OFF
|
|
|
|
with pytest.raises(ValueError):
|
|
await common.async_set_hvac_mode(hass, HVACMode.DRY)
|
|
|
|
assert thermostat.state == HVACMode.OFF
|
|
assert auth.method is None # No communication with API
|
|
|
|
|
|
async def test_thermostat_set_eco_preset(
|
|
hass: HomeAssistant,
|
|
setup_platform: PlatformSetup,
|
|
auth: FakeAuth,
|
|
create_device: CreateDevice,
|
|
create_event: CreateEvent,
|
|
) -> None:
|
|
"""Test a thermostat put into eco mode."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.ThermostatHvac": {"status": "OFF"},
|
|
"sdm.devices.traits.ThermostatEco": {
|
|
"availableModes": ["MANUAL_ECO", "OFF"],
|
|
"mode": "OFF",
|
|
"heatCelsius": 15.0,
|
|
"coolCelsius": 28.0,
|
|
},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "OFF",
|
|
},
|
|
}
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.OFF
|
|
assert thermostat.attributes[ATTR_HVAC_ACTION] == HVACAction.OFF
|
|
assert thermostat.attributes[ATTR_PRESET_MODE] == PRESET_NONE
|
|
|
|
# Turn on eco mode
|
|
await common.async_set_preset_mode(hass, PRESET_ECO)
|
|
await hass.async_block_till_done()
|
|
|
|
assert auth.method == "post"
|
|
assert auth.url == DEVICE_COMMAND
|
|
assert auth.json == {
|
|
"command": "sdm.devices.commands.ThermostatEco.SetMode",
|
|
"params": {"mode": "MANUAL_ECO"},
|
|
}
|
|
|
|
# Local state does not reflect the update
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.OFF
|
|
assert thermostat.attributes[ATTR_HVAC_ACTION] == HVACAction.OFF
|
|
assert thermostat.attributes[ATTR_PRESET_MODE] == PRESET_NONE
|
|
|
|
# Simulate pubsub message when mode changes
|
|
await create_event(
|
|
{
|
|
"sdm.devices.traits.ThermostatEco": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "MANUAL_ECO",
|
|
"heatCelsius": 15.0,
|
|
"coolCelsius": 28.0,
|
|
},
|
|
}
|
|
)
|
|
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.OFF
|
|
assert thermostat.attributes[ATTR_HVAC_ACTION] == HVACAction.OFF
|
|
assert thermostat.attributes[ATTR_PRESET_MODE] == PRESET_ECO
|
|
|
|
# Turn off eco mode
|
|
await common.async_set_preset_mode(hass, PRESET_NONE)
|
|
await hass.async_block_till_done()
|
|
|
|
assert auth.method == "post"
|
|
assert auth.url == DEVICE_COMMAND
|
|
assert auth.json == {
|
|
"command": "sdm.devices.commands.ThermostatEco.SetMode",
|
|
"params": {"mode": "OFF"},
|
|
}
|
|
|
|
# Simulate the mode changing
|
|
await create_event(
|
|
{
|
|
"sdm.devices.traits.ThermostatEco": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "OFF",
|
|
},
|
|
}
|
|
)
|
|
|
|
auth.method = None
|
|
auth.url = None
|
|
auth.json = None
|
|
|
|
# Attempting to set the preset mode when already in that mode will
|
|
# not send any messages to the API (it would otherwise fail)
|
|
await common.async_set_preset_mode(hass, PRESET_NONE)
|
|
await hass.async_block_till_done()
|
|
|
|
assert auth.method is None
|
|
assert auth.url is None
|
|
assert auth.json is None
|
|
|
|
|
|
async def test_thermostat_set_cool(
|
|
hass: HomeAssistant,
|
|
setup_platform: PlatformSetup,
|
|
auth: FakeAuth,
|
|
create_device: CreateDevice,
|
|
) -> None:
|
|
"""Test a thermostat in cool mode with a temperature change."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.ThermostatHvac": {"status": "OFF"},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "COOL",
|
|
},
|
|
"sdm.devices.traits.ThermostatTemperatureSetpoint": {
|
|
"coolCelsius": 25.0,
|
|
},
|
|
},
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.COOL
|
|
|
|
await common.async_set_temperature(hass, temperature=24.0)
|
|
await hass.async_block_till_done()
|
|
|
|
assert auth.method == "post"
|
|
assert auth.url == DEVICE_COMMAND
|
|
assert auth.json == {
|
|
"command": "sdm.devices.commands.ThermostatTemperatureSetpoint.SetCool",
|
|
"params": {"coolCelsius": 24.0},
|
|
}
|
|
|
|
|
|
async def test_thermostat_set_heat(
|
|
hass: HomeAssistant,
|
|
setup_platform: PlatformSetup,
|
|
auth: FakeAuth,
|
|
create_device: CreateDevice,
|
|
) -> None:
|
|
"""Test a thermostat heating mode with a temperature change."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.ThermostatHvac": {"status": "OFF"},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "HEAT",
|
|
},
|
|
"sdm.devices.traits.ThermostatTemperatureSetpoint": {
|
|
"heatCelsius": 19.0,
|
|
},
|
|
}
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.HEAT
|
|
|
|
await common.async_set_temperature(hass, temperature=20.0)
|
|
await hass.async_block_till_done()
|
|
|
|
assert auth.method == "post"
|
|
assert auth.url == DEVICE_COMMAND
|
|
assert auth.json == {
|
|
"command": "sdm.devices.commands.ThermostatTemperatureSetpoint.SetHeat",
|
|
"params": {"heatCelsius": 20.0},
|
|
}
|
|
|
|
|
|
async def test_thermostat_set_temperature_hvac_mode(
|
|
hass: HomeAssistant,
|
|
setup_platform: PlatformSetup,
|
|
auth: FakeAuth,
|
|
create_device: CreateDevice,
|
|
) -> None:
|
|
"""Test setting HVAC mode while setting temperature."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.ThermostatHvac": {"status": "OFF"},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "OFF",
|
|
},
|
|
"sdm.devices.traits.ThermostatTemperatureSetpoint": {
|
|
"coolCelsius": 25.0,
|
|
},
|
|
},
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.OFF
|
|
|
|
await common.async_set_temperature(hass, temperature=24.0, hvac_mode=HVACMode.COOL)
|
|
await hass.async_block_till_done()
|
|
|
|
assert auth.method == "post"
|
|
assert auth.url == DEVICE_COMMAND
|
|
assert auth.json == {
|
|
"command": "sdm.devices.commands.ThermostatTemperatureSetpoint.SetCool",
|
|
"params": {"coolCelsius": 24.0},
|
|
}
|
|
|
|
await common.async_set_temperature(hass, temperature=26.0, hvac_mode=HVACMode.HEAT)
|
|
await hass.async_block_till_done()
|
|
|
|
assert auth.method == "post"
|
|
assert auth.url == DEVICE_COMMAND
|
|
assert auth.json == {
|
|
"command": "sdm.devices.commands.ThermostatTemperatureSetpoint.SetHeat",
|
|
"params": {"heatCelsius": 26.0},
|
|
}
|
|
|
|
await common.async_set_temperature(
|
|
hass, target_temp_low=20.0, target_temp_high=24.0, hvac_mode=HVACMode.HEAT_COOL
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
assert auth.method == "post"
|
|
assert auth.url == DEVICE_COMMAND
|
|
assert auth.json == {
|
|
"command": "sdm.devices.commands.ThermostatTemperatureSetpoint.SetRange",
|
|
"params": {"heatCelsius": 20.0, "coolCelsius": 24.0},
|
|
}
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("setpoint", "target_low", "target_high", "expected_params"),
|
|
[
|
|
(
|
|
{
|
|
"heatCelsius": 19.0,
|
|
"coolCelsius": 25.0,
|
|
},
|
|
19.0,
|
|
20.0,
|
|
# Cool is accepted and lowers heat by the min range
|
|
{"heatCelsius": 18.33333, "coolCelsius": 20.0},
|
|
),
|
|
(
|
|
{
|
|
"heatCelsius": 19.0,
|
|
"coolCelsius": 25.0,
|
|
},
|
|
24.0,
|
|
25.0,
|
|
# Cool is accepted and lowers heat by the min range
|
|
{"heatCelsius": 24.0, "coolCelsius": 25.66667},
|
|
),
|
|
],
|
|
)
|
|
async def test_thermostat_set_temperature_range_too_close(
|
|
hass: HomeAssistant,
|
|
setup_platform: PlatformSetup,
|
|
auth: FakeAuth,
|
|
create_device: CreateDevice,
|
|
setpoint: dict[str, Any],
|
|
target_low: float,
|
|
target_high: float,
|
|
expected_params: dict[str, Any],
|
|
) -> None:
|
|
"""Test setting an HVAC temperature range that is too small of a range."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.ThermostatHvac": {"status": "OFF"},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "HEATCOOL",
|
|
},
|
|
"sdm.devices.traits.ThermostatTemperatureSetpoint": setpoint,
|
|
},
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.HEAT_COOL
|
|
|
|
# Move the target temp to be in too small of a range
|
|
await common.async_set_temperature(
|
|
hass,
|
|
target_temp_low=target_low,
|
|
target_temp_high=target_high,
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
assert auth.method == "post"
|
|
assert auth.url == DEVICE_COMMAND
|
|
assert auth.json == {
|
|
"command": "sdm.devices.commands.ThermostatTemperatureSetpoint.SetRange",
|
|
"params": expected_params,
|
|
}
|
|
|
|
|
|
async def test_thermostat_set_heat_cool(
|
|
hass: HomeAssistant,
|
|
setup_platform: PlatformSetup,
|
|
auth: FakeAuth,
|
|
create_device: CreateDevice,
|
|
) -> None:
|
|
"""Test a thermostat in heatcool mode with a temperature change."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.ThermostatHvac": {"status": "OFF"},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "HEATCOOL",
|
|
},
|
|
"sdm.devices.traits.ThermostatTemperatureSetpoint": {
|
|
"heatCelsius": 19.0,
|
|
"coolCelsius": 25.0,
|
|
},
|
|
}
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.HEAT_COOL
|
|
|
|
await common.async_set_temperature(
|
|
hass, target_temp_low=20.0, target_temp_high=24.0
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
assert auth.method == "post"
|
|
assert auth.url == DEVICE_COMMAND
|
|
assert auth.json == {
|
|
"command": "sdm.devices.commands.ThermostatTemperatureSetpoint.SetRange",
|
|
"params": {"heatCelsius": 20.0, "coolCelsius": 24.0},
|
|
}
|
|
|
|
|
|
async def test_thermostat_fan_off(
|
|
hass: HomeAssistant,
|
|
setup_platform: PlatformSetup,
|
|
create_device: CreateDevice,
|
|
) -> None:
|
|
"""Test a thermostat with the fan not running."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.Fan": {
|
|
"timerMode": "OFF",
|
|
"timerTimeout": "2019-05-10T03:22:54Z",
|
|
},
|
|
"sdm.devices.traits.ThermostatHvac": {"status": "OFF"},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "COOL",
|
|
},
|
|
"sdm.devices.traits.Temperature": {
|
|
"ambientTemperatureCelsius": 16.2,
|
|
},
|
|
}
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.COOL
|
|
assert thermostat.attributes[ATTR_HVAC_ACTION] == HVACAction.IDLE
|
|
assert thermostat.attributes[ATTR_CURRENT_TEMPERATURE] == 16.2
|
|
assert set(thermostat.attributes[ATTR_HVAC_MODES]) == {
|
|
HVACMode.HEAT,
|
|
HVACMode.COOL,
|
|
HVACMode.HEAT_COOL,
|
|
HVACMode.OFF,
|
|
}
|
|
assert thermostat.attributes[ATTR_FAN_MODE] == FAN_OFF
|
|
assert thermostat.attributes[ATTR_FAN_MODES] == [FAN_ON, FAN_OFF]
|
|
assert thermostat.attributes[ATTR_SUPPORTED_FEATURES] == (
|
|
ClimateEntityFeature.TARGET_TEMPERATURE
|
|
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
|
|
| ClimateEntityFeature.FAN_MODE
|
|
| ClimateEntityFeature.TURN_OFF
|
|
| ClimateEntityFeature.TURN_ON
|
|
)
|
|
|
|
|
|
async def test_thermostat_fan_on(
|
|
hass: HomeAssistant,
|
|
setup_platform: PlatformSetup,
|
|
create_device: CreateDevice,
|
|
) -> None:
|
|
"""Test a thermostat with the fan running."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.Fan": {
|
|
"timerMode": "ON",
|
|
"timerTimeout": "2019-05-10T03:22:54Z",
|
|
},
|
|
"sdm.devices.traits.ThermostatHvac": {
|
|
"status": "OFF",
|
|
},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "COOL",
|
|
},
|
|
"sdm.devices.traits.Temperature": {
|
|
"ambientTemperatureCelsius": 16.2,
|
|
},
|
|
}
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.COOL
|
|
assert thermostat.attributes[ATTR_HVAC_ACTION] == HVACAction.IDLE
|
|
assert thermostat.attributes[ATTR_CURRENT_TEMPERATURE] == 16.2
|
|
assert set(thermostat.attributes[ATTR_HVAC_MODES]) == {
|
|
HVACMode.HEAT,
|
|
HVACMode.COOL,
|
|
HVACMode.HEAT_COOL,
|
|
HVACMode.OFF,
|
|
}
|
|
assert thermostat.attributes[ATTR_FAN_MODE] == FAN_ON
|
|
assert thermostat.attributes[ATTR_FAN_MODES] == [FAN_ON, FAN_OFF]
|
|
assert thermostat.attributes[ATTR_SUPPORTED_FEATURES] == (
|
|
ClimateEntityFeature.TARGET_TEMPERATURE
|
|
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
|
|
| ClimateEntityFeature.FAN_MODE
|
|
| ClimateEntityFeature.TURN_OFF
|
|
| ClimateEntityFeature.TURN_ON
|
|
)
|
|
|
|
|
|
async def test_thermostat_cool_with_fan(
|
|
hass: HomeAssistant,
|
|
setup_platform: PlatformSetup,
|
|
create_device: CreateDevice,
|
|
) -> None:
|
|
"""Test a thermostat cooling while the fan is on."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.Fan": {
|
|
"timerMode": "ON",
|
|
"timerTimeout": "2019-05-10T03:22:54Z",
|
|
},
|
|
"sdm.devices.traits.ThermostatHvac": {
|
|
"status": "OFF",
|
|
},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "COOL",
|
|
},
|
|
},
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.COOL
|
|
assert thermostat.attributes[ATTR_HVAC_ACTION] == HVACAction.IDLE
|
|
assert set(thermostat.attributes[ATTR_HVAC_MODES]) == {
|
|
HVACMode.HEAT,
|
|
HVACMode.COOL,
|
|
HVACMode.HEAT_COOL,
|
|
HVACMode.OFF,
|
|
}
|
|
assert thermostat.attributes[ATTR_FAN_MODE] == FAN_ON
|
|
assert thermostat.attributes[ATTR_FAN_MODES] == [FAN_ON, FAN_OFF]
|
|
assert thermostat.attributes[ATTR_SUPPORTED_FEATURES] == (
|
|
ClimateEntityFeature.TARGET_TEMPERATURE
|
|
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
|
|
| ClimateEntityFeature.FAN_MODE
|
|
| ClimateEntityFeature.TURN_OFF
|
|
| ClimateEntityFeature.TURN_ON
|
|
)
|
|
|
|
|
|
async def test_thermostat_set_fan(
|
|
hass: HomeAssistant,
|
|
setup_platform: PlatformSetup,
|
|
auth: FakeAuth,
|
|
create_device: CreateDevice,
|
|
) -> None:
|
|
"""Test a thermostat enabling the fan."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.Fan": {
|
|
"timerMode": "ON",
|
|
"timerTimeout": "2019-05-10T03:22:54Z",
|
|
},
|
|
"sdm.devices.traits.ThermostatHvac": {
|
|
"status": "OFF",
|
|
},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "HEAT",
|
|
},
|
|
}
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.HEAT
|
|
assert thermostat.attributes[ATTR_FAN_MODE] == FAN_ON
|
|
assert thermostat.attributes[ATTR_FAN_MODES] == [FAN_ON, FAN_OFF]
|
|
assert thermostat.attributes[ATTR_SUPPORTED_FEATURES] == (
|
|
ClimateEntityFeature.TARGET_TEMPERATURE
|
|
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
|
|
| ClimateEntityFeature.FAN_MODE
|
|
| ClimateEntityFeature.TURN_OFF
|
|
| ClimateEntityFeature.TURN_ON
|
|
)
|
|
|
|
# Turn off fan mode
|
|
await common.async_set_fan_mode(hass, FAN_OFF)
|
|
await hass.async_block_till_done()
|
|
|
|
assert auth.method == "post"
|
|
assert auth.url == DEVICE_COMMAND
|
|
assert auth.json == {
|
|
"command": "sdm.devices.commands.Fan.SetTimer",
|
|
"params": {"timerMode": "OFF"},
|
|
}
|
|
|
|
# Turn on fan mode
|
|
await common.async_set_fan_mode(hass, FAN_ON)
|
|
await hass.async_block_till_done()
|
|
|
|
assert auth.method == "post"
|
|
assert auth.url == DEVICE_COMMAND
|
|
assert auth.json == {
|
|
"command": "sdm.devices.commands.Fan.SetTimer",
|
|
"params": {
|
|
"duration": "43200s",
|
|
"timerMode": "ON",
|
|
},
|
|
}
|
|
|
|
|
|
async def test_thermostat_set_fan_when_off(
|
|
hass: HomeAssistant,
|
|
setup_platform: PlatformSetup,
|
|
auth: FakeAuth,
|
|
create_device: CreateDevice,
|
|
) -> None:
|
|
"""Test a thermostat enabling the fan."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.Fan": {
|
|
"timerMode": "ON",
|
|
"timerTimeout": "2019-05-10T03:22:54Z",
|
|
},
|
|
"sdm.devices.traits.ThermostatHvac": {
|
|
"status": "OFF",
|
|
},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "OFF",
|
|
},
|
|
}
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.OFF
|
|
assert thermostat.attributes[ATTR_FAN_MODE] == FAN_ON
|
|
assert thermostat.attributes[ATTR_FAN_MODES] == [FAN_ON, FAN_OFF]
|
|
assert thermostat.attributes[ATTR_SUPPORTED_FEATURES] == (
|
|
ClimateEntityFeature.TARGET_TEMPERATURE
|
|
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
|
|
| ClimateEntityFeature.FAN_MODE
|
|
| ClimateEntityFeature.TURN_OFF
|
|
| ClimateEntityFeature.TURN_ON
|
|
)
|
|
|
|
# Fan cannot be turned on when HVAC is off
|
|
with pytest.raises(ValueError):
|
|
await common.async_set_fan_mode(hass, FAN_ON, entity_id="climate.my_thermostat")
|
|
|
|
|
|
async def test_thermostat_fan_empty(
|
|
hass: HomeAssistant,
|
|
setup_platform: PlatformSetup,
|
|
create_device: CreateDevice,
|
|
) -> None:
|
|
"""Test a fan trait with an empty response."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.Fan": {},
|
|
"sdm.devices.traits.ThermostatHvac": {"status": "OFF"},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "OFF",
|
|
},
|
|
"sdm.devices.traits.Temperature": {
|
|
"ambientTemperatureCelsius": 16.2,
|
|
},
|
|
}
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.OFF
|
|
assert thermostat.attributes[ATTR_HVAC_ACTION] == HVACAction.OFF
|
|
assert thermostat.attributes[ATTR_CURRENT_TEMPERATURE] == 16.2
|
|
assert set(thermostat.attributes[ATTR_HVAC_MODES]) == {
|
|
HVACMode.HEAT,
|
|
HVACMode.COOL,
|
|
HVACMode.HEAT_COOL,
|
|
HVACMode.OFF,
|
|
}
|
|
assert ATTR_FAN_MODE not in thermostat.attributes
|
|
assert ATTR_FAN_MODES not in thermostat.attributes
|
|
assert thermostat.attributes[ATTR_SUPPORTED_FEATURES] == (
|
|
ClimateEntityFeature.TARGET_TEMPERATURE
|
|
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
|
|
| ClimateEntityFeature.TURN_OFF
|
|
| ClimateEntityFeature.TURN_ON
|
|
)
|
|
|
|
# Ignores set_fan_mode since it is lacking SUPPORT_FAN_MODE
|
|
await common.async_set_fan_mode(hass, FAN_ON)
|
|
await hass.async_block_till_done()
|
|
|
|
assert ATTR_FAN_MODE not in thermostat.attributes
|
|
assert ATTR_FAN_MODES not in thermostat.attributes
|
|
|
|
|
|
async def test_thermostat_invalid_fan_mode(
|
|
hass: HomeAssistant,
|
|
setup_platform: PlatformSetup,
|
|
create_device: CreateDevice,
|
|
) -> None:
|
|
"""Test setting a fan mode that is not supported."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.Fan": {
|
|
"timerMode": "ON",
|
|
"timerTimeout": "2019-05-10T03:22:54Z",
|
|
},
|
|
"sdm.devices.traits.ThermostatHvac": {"status": "OFF"},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "COOL",
|
|
},
|
|
"sdm.devices.traits.Temperature": {
|
|
"ambientTemperatureCelsius": 16.2,
|
|
},
|
|
}
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.COOL
|
|
assert thermostat.attributes[ATTR_HVAC_ACTION] == HVACAction.IDLE
|
|
assert thermostat.attributes[ATTR_CURRENT_TEMPERATURE] == 16.2
|
|
assert set(thermostat.attributes[ATTR_HVAC_MODES]) == {
|
|
HVACMode.HEAT,
|
|
HVACMode.COOL,
|
|
HVACMode.HEAT_COOL,
|
|
HVACMode.OFF,
|
|
}
|
|
assert thermostat.attributes[ATTR_FAN_MODE] == FAN_ON
|
|
assert thermostat.attributes[ATTR_FAN_MODES] == [FAN_ON, FAN_OFF]
|
|
|
|
with pytest.raises(ServiceValidationError):
|
|
await common.async_set_fan_mode(hass, FAN_LOW)
|
|
|
|
|
|
async def test_thermostat_target_temp(
|
|
hass: HomeAssistant,
|
|
setup_platform: PlatformSetup,
|
|
create_device: CreateDevice,
|
|
create_event: CreateEvent,
|
|
) -> None:
|
|
"""Test a thermostat changing hvac modes and affected on target temps."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.ThermostatHvac": {
|
|
"status": "HEATING",
|
|
},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "HEAT",
|
|
},
|
|
"sdm.devices.traits.Temperature": {
|
|
"ambientTemperatureCelsius": 20.1,
|
|
},
|
|
"sdm.devices.traits.ThermostatTemperatureSetpoint": {
|
|
"heatCelsius": 23.0,
|
|
},
|
|
}
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.HEAT
|
|
assert thermostat.attributes[ATTR_TEMPERATURE] == 23.0
|
|
assert thermostat.attributes[ATTR_TARGET_TEMP_LOW] is None
|
|
assert thermostat.attributes[ATTR_TARGET_TEMP_HIGH] is None
|
|
|
|
# Simulate pubsub message changing modes
|
|
await create_event(
|
|
{
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "HEATCOOL",
|
|
},
|
|
"sdm.devices.traits.ThermostatTemperatureSetpoint": {
|
|
"heatCelsius": 22.0,
|
|
"coolCelsius": 28.0,
|
|
},
|
|
}
|
|
)
|
|
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.HEAT_COOL
|
|
assert thermostat.attributes[ATTR_TARGET_TEMP_LOW] == 22.0
|
|
assert thermostat.attributes[ATTR_TARGET_TEMP_HIGH] == 28.0
|
|
assert thermostat.attributes[ATTR_TEMPERATURE] is None
|
|
|
|
|
|
async def test_thermostat_missing_mode_traits(
|
|
hass: HomeAssistant,
|
|
setup_platform: PlatformSetup,
|
|
create_device: CreateDevice,
|
|
) -> None:
|
|
"""Test a thermostat missing many thermostat traits in api response."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.ThermostatHvac": {"status": "OFF"},
|
|
}
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.OFF
|
|
assert thermostat.attributes[ATTR_HVAC_ACTION] == HVACAction.OFF
|
|
assert thermostat.attributes[ATTR_CURRENT_TEMPERATURE] is None
|
|
assert set(thermostat.attributes[ATTR_HVAC_MODES]) == set()
|
|
assert ATTR_TEMPERATURE not in thermostat.attributes
|
|
assert ATTR_TARGET_TEMP_LOW not in thermostat.attributes
|
|
assert ATTR_TARGET_TEMP_HIGH not in thermostat.attributes
|
|
assert ATTR_PRESET_MODE not in thermostat.attributes
|
|
assert ATTR_PRESET_MODES not in thermostat.attributes
|
|
assert ATTR_FAN_MODE not in thermostat.attributes
|
|
assert ATTR_FAN_MODES not in thermostat.attributes
|
|
|
|
await common.async_set_temperature(hass, temperature=24.0)
|
|
await hass.async_block_till_done()
|
|
assert ATTR_TEMPERATURE not in thermostat.attributes
|
|
|
|
await common.async_set_preset_mode(hass, PRESET_ECO)
|
|
await hass.async_block_till_done()
|
|
assert ATTR_PRESET_MODE not in thermostat.attributes
|
|
|
|
|
|
async def test_thermostat_missing_temperature_trait(
|
|
hass: HomeAssistant,
|
|
setup_platform: PlatformSetup,
|
|
create_device: CreateDevice,
|
|
) -> None:
|
|
"""Test a thermostat missing many thermostat traits in api response."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.ThermostatHvac": {"status": "OFF"},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "HEAT",
|
|
},
|
|
}
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.HEAT
|
|
assert thermostat.attributes[ATTR_HVAC_ACTION] == HVACAction.IDLE
|
|
assert thermostat.attributes[ATTR_CURRENT_TEMPERATURE] is None
|
|
assert set(thermostat.attributes[ATTR_HVAC_MODES]) == {
|
|
HVACMode.HEAT,
|
|
HVACMode.COOL,
|
|
HVACMode.HEAT_COOL,
|
|
HVACMode.OFF,
|
|
}
|
|
assert thermostat.attributes[ATTR_TEMPERATURE] is None
|
|
assert thermostat.attributes[ATTR_TARGET_TEMP_LOW] is None
|
|
assert thermostat.attributes[ATTR_TARGET_TEMP_HIGH] is None
|
|
assert ATTR_PRESET_MODE not in thermostat.attributes
|
|
assert ATTR_PRESET_MODES not in thermostat.attributes
|
|
assert ATTR_FAN_MODE not in thermostat.attributes
|
|
assert ATTR_FAN_MODES not in thermostat.attributes
|
|
|
|
with pytest.raises(HomeAssistantError) as e_info:
|
|
await common.async_set_temperature(hass, temperature=24.0)
|
|
await hass.async_block_till_done()
|
|
assert "temperature" in str(e_info)
|
|
assert "climate.my_thermostat" in str(e_info)
|
|
assert "24.0" in str(e_info)
|
|
assert thermostat.attributes[ATTR_TEMPERATURE] is None
|
|
|
|
|
|
async def test_thermostat_unexpected_hvac_status(
|
|
hass: HomeAssistant,
|
|
setup_platform: PlatformSetup,
|
|
create_device: CreateDevice,
|
|
) -> None:
|
|
"""Test a thermostat missing many thermostat traits in api response."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.ThermostatHvac": {"status": "UNEXPECTED"},
|
|
}
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.OFF
|
|
assert ATTR_HVAC_ACTION not in thermostat.attributes
|
|
assert thermostat.attributes[ATTR_CURRENT_TEMPERATURE] is None
|
|
assert set(thermostat.attributes[ATTR_HVAC_MODES]) == set()
|
|
assert ATTR_TEMPERATURE not in thermostat.attributes
|
|
assert ATTR_TARGET_TEMP_LOW not in thermostat.attributes
|
|
assert ATTR_TARGET_TEMP_HIGH not in thermostat.attributes
|
|
assert ATTR_PRESET_MODE not in thermostat.attributes
|
|
assert ATTR_PRESET_MODES not in thermostat.attributes
|
|
assert ATTR_FAN_MODE not in thermostat.attributes
|
|
assert ATTR_FAN_MODES not in thermostat.attributes
|
|
|
|
with pytest.raises(ValueError):
|
|
await common.async_set_hvac_mode(hass, HVACMode.DRY)
|
|
assert thermostat.state == HVACMode.OFF
|
|
|
|
|
|
async def test_thermostat_missing_set_point(
|
|
hass: HomeAssistant,
|
|
setup_platform: PlatformSetup,
|
|
create_device: CreateDevice,
|
|
) -> None:
|
|
"""Test a thermostat missing many thermostat traits in api response."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.ThermostatHvac": {"status": "OFF"},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "HEATCOOL",
|
|
},
|
|
},
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.HEAT_COOL
|
|
assert thermostat.attributes[ATTR_HVAC_ACTION] == HVACAction.IDLE
|
|
assert thermostat.attributes[ATTR_CURRENT_TEMPERATURE] is None
|
|
assert set(thermostat.attributes[ATTR_HVAC_MODES]) == {
|
|
HVACMode.HEAT,
|
|
HVACMode.COOL,
|
|
HVACMode.HEAT_COOL,
|
|
HVACMode.OFF,
|
|
}
|
|
assert thermostat.attributes[ATTR_TEMPERATURE] is None
|
|
assert thermostat.attributes[ATTR_TARGET_TEMP_LOW] is None
|
|
assert thermostat.attributes[ATTR_TARGET_TEMP_HIGH] is None
|
|
assert ATTR_PRESET_MODE not in thermostat.attributes
|
|
assert ATTR_PRESET_MODES not in thermostat.attributes
|
|
assert ATTR_FAN_MODE not in thermostat.attributes
|
|
assert ATTR_FAN_MODES not in thermostat.attributes
|
|
|
|
|
|
async def test_thermostat_unexepected_hvac_mode(
|
|
hass: HomeAssistant,
|
|
setup_platform: PlatformSetup,
|
|
create_device: CreateDevice,
|
|
) -> None:
|
|
"""Test a thermostat missing many thermostat traits in api response."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.ThermostatHvac": {"status": "OFF"},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF", "UNEXPECTED"],
|
|
"mode": "UNEXPECTED",
|
|
},
|
|
}
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.OFF
|
|
assert thermostat.attributes[ATTR_HVAC_ACTION] == HVACAction.OFF
|
|
assert thermostat.attributes[ATTR_CURRENT_TEMPERATURE] is None
|
|
assert set(thermostat.attributes[ATTR_HVAC_MODES]) == {
|
|
HVACMode.HEAT,
|
|
HVACMode.COOL,
|
|
HVACMode.HEAT_COOL,
|
|
HVACMode.OFF,
|
|
}
|
|
assert thermostat.attributes[ATTR_TEMPERATURE] is None
|
|
assert thermostat.attributes[ATTR_TARGET_TEMP_LOW] is None
|
|
assert thermostat.attributes[ATTR_TARGET_TEMP_HIGH] is None
|
|
assert ATTR_PRESET_MODE not in thermostat.attributes
|
|
assert ATTR_PRESET_MODES not in thermostat.attributes
|
|
assert ATTR_FAN_MODE not in thermostat.attributes
|
|
assert ATTR_FAN_MODES not in thermostat.attributes
|
|
|
|
|
|
async def test_thermostat_invalid_set_preset_mode(
|
|
hass: HomeAssistant,
|
|
setup_platform: PlatformSetup,
|
|
auth: FakeAuth,
|
|
create_device: CreateDevice,
|
|
) -> None:
|
|
"""Test a thermostat set with an invalid preset mode."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.ThermostatHvac": {"status": "OFF"},
|
|
"sdm.devices.traits.ThermostatEco": {
|
|
"availableModes": ["MANUAL_ECO", "OFF"],
|
|
"mode": "OFF",
|
|
"heatCelsius": 15.0,
|
|
"coolCelsius": 28.0,
|
|
},
|
|
}
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.OFF
|
|
assert thermostat.attributes[ATTR_PRESET_MODE] == PRESET_NONE
|
|
assert thermostat.attributes[ATTR_PRESET_MODES] == [PRESET_ECO, PRESET_NONE]
|
|
|
|
# Set preset mode that is invalid
|
|
with pytest.raises(ServiceValidationError):
|
|
await common.async_set_preset_mode(hass, PRESET_SLEEP)
|
|
|
|
# No RPC sent
|
|
assert auth.method is None
|
|
|
|
# Preset is unchanged
|
|
assert thermostat.attributes[ATTR_PRESET_MODE] == PRESET_NONE
|
|
assert thermostat.attributes[ATTR_PRESET_MODES] == [PRESET_ECO, PRESET_NONE]
|
|
|
|
|
|
async def test_thermostat_hvac_mode_failure(
|
|
hass: HomeAssistant,
|
|
setup_platform: PlatformSetup,
|
|
auth: FakeAuth,
|
|
create_device: CreateDevice,
|
|
) -> None:
|
|
"""Test setting an hvac_mode that is not supported."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.ThermostatHvac": {"status": "OFF"},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "COOL",
|
|
},
|
|
"sdm.devices.traits.ThermostatTemperatureSetpoint": {
|
|
"coolCelsius": 25.0,
|
|
},
|
|
"sdm.devices.traits.Fan": {
|
|
"timerMode": "OFF",
|
|
"timerTimeout": "2019-05-10T03:22:54Z",
|
|
},
|
|
"sdm.devices.traits.ThermostatEco": {
|
|
"availableModes": ["MANUAL_ECO", "OFF"],
|
|
"mode": "OFF",
|
|
"heatCelsius": 15.0,
|
|
"coolCelsius": 28.0,
|
|
},
|
|
}
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.COOL
|
|
assert thermostat.attributes[ATTR_HVAC_ACTION] == HVACAction.IDLE
|
|
|
|
auth.responses = [aiohttp.web.Response(status=HTTPStatus.BAD_REQUEST)]
|
|
with pytest.raises(HomeAssistantError) as e_info:
|
|
await common.async_set_hvac_mode(hass, HVACMode.HEAT)
|
|
assert "HVAC mode" in str(e_info)
|
|
assert "climate.my_thermostat" in str(e_info)
|
|
assert HVACMode.HEAT in str(e_info)
|
|
|
|
auth.responses = [aiohttp.web.Response(status=HTTPStatus.BAD_REQUEST)]
|
|
with pytest.raises(HomeAssistantError) as e_info:
|
|
await common.async_set_temperature(hass, temperature=25.0)
|
|
assert "temperature" in str(e_info)
|
|
assert "climate.my_thermostat" in str(e_info)
|
|
assert "25.0" in str(e_info)
|
|
|
|
auth.responses = [aiohttp.web.Response(status=HTTPStatus.BAD_REQUEST)]
|
|
with pytest.raises(HomeAssistantError) as e_info:
|
|
await common.async_set_fan_mode(hass, FAN_ON)
|
|
assert "fan mode" in str(e_info)
|
|
assert "climate.my_thermostat" in str(e_info)
|
|
assert FAN_ON in str(e_info)
|
|
|
|
auth.responses = [aiohttp.web.Response(status=HTTPStatus.BAD_REQUEST)]
|
|
with pytest.raises(HomeAssistantError) as e_info:
|
|
await common.async_set_preset_mode(hass, PRESET_ECO)
|
|
assert "preset mode" in str(e_info)
|
|
assert "climate.my_thermostat" in str(e_info)
|
|
assert PRESET_ECO in str(e_info)
|
|
|
|
|
|
async def test_thermostat_available(
|
|
hass: HomeAssistant, setup_platform: PlatformSetup, create_device: CreateDevice
|
|
) -> None:
|
|
"""Test a thermostat that is available."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.ThermostatHvac": {
|
|
"status": "COOLING",
|
|
},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "COOL",
|
|
},
|
|
"sdm.devices.traits.Temperature": {
|
|
"ambientTemperatureCelsius": 29.9,
|
|
},
|
|
"sdm.devices.traits.ThermostatTemperatureSetpoint": {
|
|
"coolCelsius": 28.0,
|
|
},
|
|
"sdm.devices.traits.Connectivity": {"status": "ONLINE"},
|
|
},
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == HVACMode.COOL
|
|
|
|
|
|
async def test_thermostat_unavailable(
|
|
hass: HomeAssistant, setup_platform: PlatformSetup, create_device: CreateDevice
|
|
) -> None:
|
|
"""Test a thermostat that is unavailable."""
|
|
create_device.create(
|
|
{
|
|
"sdm.devices.traits.ThermostatHvac": {
|
|
"status": "COOLING",
|
|
},
|
|
"sdm.devices.traits.ThermostatMode": {
|
|
"availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"],
|
|
"mode": "COOL",
|
|
},
|
|
"sdm.devices.traits.Temperature": {
|
|
"ambientTemperatureCelsius": 29.9,
|
|
},
|
|
"sdm.devices.traits.ThermostatTemperatureSetpoint": {
|
|
"coolCelsius": 28.0,
|
|
},
|
|
"sdm.devices.traits.Connectivity": {"status": "OFFLINE"},
|
|
},
|
|
)
|
|
await setup_platform()
|
|
|
|
assert len(hass.states.async_all()) == 1
|
|
thermostat = hass.states.get("climate.my_thermostat")
|
|
assert thermostat is not None
|
|
assert thermostat.state == STATE_UNAVAILABLE
|