* NEST - Issues with lost internet connectivity #70479 Update Climate and Sensor entities to be unavailable when the device connectivity trait indicates the device is offline. The prior behavior, the last known values would be displayed indefinitely if the device lost internet connectivity. This was creating the illusion that the device was still connected. With this change, the Home Assistant entities will become unavailable when the device loses connectivity. * Update formatting * Add doc strings, fix indentation * Fix doc strings * Update test_climate_sdm.py * Update test_climate_sdm.py * Update test_sensor_sdm.py * Update test_sensor_sdm.py * more formatting fixes * Place availability logic in mixin 1. Consolidate repeated code into mixin and apply mixin to Climate and Sensor entities 2. Return true instead of super.available() 3. No unit test changes required to maintain code coverage * Define self._device is mixin to make linter happier * Remove logger used for debugging * restore whitespace * Fix test due to underlying merge change * Update availability_mixin.py * Move availability logic into device_info * Update sensor_sdm.py
1508 lines
51 KiB
Python
1508 lines
51 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
|
|
|
|
from .common import (
|
|
DEVICE_COMMAND,
|
|
DEVICE_ID,
|
|
CreateDevice,
|
|
FakeSubscriber,
|
|
PlatformSetup,
|
|
)
|
|
from .conftest import FakeAuth
|
|
|
|
from tests.components.climate import common
|
|
|
|
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(
|
|
{
|
|
"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
|
|
):
|
|
"""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
|
|
):
|
|
"""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
|
|
):
|
|
"""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
|
|
):
|
|
"""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)
|
|
await hass.async_block_till_done()
|
|
|
|
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"},
|
|
}
|
|
|
|
|
|
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},
|
|
}
|
|
|
|
|
|
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
|
|
)
|
|
|
|
|
|
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
|
|
)
|
|
|
|
|
|
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
|
|
)
|
|
|
|
|
|
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
|
|
)
|
|
|
|
# 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
|
|
)
|
|
|
|
# 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
|
|
)
|
|
|
|
# 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(ValueError):
|
|
await common.async_set_fan_mode(hass, FAN_LOW)
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
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
|
|
|
|
await common.async_set_temperature(hass, temperature=24.0)
|
|
await hass.async_block_till_done()
|
|
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)
|
|
await hass.async_block_till_done()
|
|
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(ValueError):
|
|
await common.async_set_preset_mode(hass, PRESET_SLEEP)
|
|
await hass.async_block_till_done()
|
|
|
|
# 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.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):
|
|
await common.async_set_hvac_mode(hass, HVACMode.HEAT)
|
|
await hass.async_block_till_done()
|
|
|
|
auth.responses = [aiohttp.web.Response(status=HTTPStatus.BAD_REQUEST)]
|
|
with pytest.raises(HomeAssistantError):
|
|
await common.async_set_temperature(
|
|
hass, hvac_mode=HVACMode.HEAT, temperature=25.0
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
auth.responses = [aiohttp.web.Response(status=HTTPStatus.BAD_REQUEST)]
|
|
with pytest.raises(HomeAssistantError):
|
|
await common.async_set_fan_mode(hass, FAN_ON)
|
|
await hass.async_block_till_done()
|
|
|
|
auth.responses = [aiohttp.web.Response(status=HTTPStatus.BAD_REQUEST)]
|
|
with pytest.raises(HomeAssistantError):
|
|
await common.async_set_preset_mode(hass, PRESET_ECO)
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
async def test_thermostat_available(
|
|
hass: HomeAssistant, setup_platform: PlatformSetup, create_device: CreateDevice
|
|
):
|
|
"""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
|
|
):
|
|
"""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
|