mirror of
https://github.com/home-assistant/core.git
synced 2026-05-20 07:20:14 +01:00
700 lines
23 KiB
Python
700 lines
23 KiB
Python
"""Tests for the MyNeomitis climate component."""
|
|
|
|
from unittest.mock import AsyncMock, call
|
|
|
|
import pytest
|
|
from syrupy.assertion import SnapshotAssertion
|
|
|
|
from homeassistant.components.climate import HVACMode
|
|
from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
|
from homeassistant.helpers import entity_registry as er
|
|
|
|
from tests.common import MockConfigEntry, snapshot_platform
|
|
|
|
CLIMATE_DEVICE = {
|
|
"_id": "climate1",
|
|
"name": "Climate Device",
|
|
"model": "EV30",
|
|
"state": {
|
|
"currentTemp": 21.5,
|
|
"overrideTemp": 22.0,
|
|
"targetMode": 1,
|
|
"comfLimitMin": 7,
|
|
"comfLimitMax": 30,
|
|
"connected": True,
|
|
},
|
|
"connected": True,
|
|
"program": {"data": {}},
|
|
}
|
|
|
|
CLIMATE_SUB_DEVICE = {
|
|
"_id": "climate_sub1",
|
|
"name": "Climate Sub Device",
|
|
"model": "NTD",
|
|
"state": {
|
|
"currentTemp": 19.0,
|
|
"targetTemp": 20.0,
|
|
"targetMode": 1,
|
|
"comfLimitMin": 7,
|
|
"comfLimitMax": 30,
|
|
"connected": True,
|
|
},
|
|
"connected": True,
|
|
"parents": {"gateway": "gw-1"},
|
|
"rfid": "rfid-1",
|
|
"program": {"data": {}},
|
|
}
|
|
|
|
CLIMATE_NTD_COOL = {
|
|
"_id": "climate_ntd1",
|
|
"name": "Climate NTD",
|
|
"model": "NTD",
|
|
"state": {
|
|
"currentTemp": 20.0,
|
|
"targetTemp": 21.0,
|
|
"targetMode": 1,
|
|
"comfLimitMin": 7,
|
|
"comfLimitMax": 30,
|
|
"changeOverUser": 1,
|
|
"connected": True,
|
|
},
|
|
"connected": True,
|
|
"parents": {"gateway": "gw-ntd"},
|
|
"rfid": "rfid-ntd",
|
|
"program": {"data": {}},
|
|
}
|
|
|
|
|
|
async def test_entities(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyaxenco_client: AsyncMock,
|
|
snapshot: SnapshotAssertion,
|
|
entity_registry: er.EntityRegistry,
|
|
) -> None:
|
|
"""Test climate entity is created for supported device."""
|
|
mock_pyaxenco_client.get_devices.return_value = [CLIMATE_DEVICE]
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
entity_registry.async_update_entity("climate.climate_device", aliases=set())
|
|
|
|
await snapshot_platform(
|
|
hass,
|
|
entity_registry,
|
|
snapshot,
|
|
mock_config_entry.entry_id,
|
|
)
|
|
|
|
|
|
async def test_set_temperature(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyaxenco_client: AsyncMock,
|
|
) -> None:
|
|
"""Test setting target temperature."""
|
|
mock_pyaxenco_client.get_devices.return_value = [CLIMATE_DEVICE]
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
entity_id = "climate.climate_device"
|
|
|
|
await hass.services.async_call(
|
|
"climate",
|
|
"set_temperature",
|
|
{ATTR_ENTITY_ID: entity_id, ATTR_TEMPERATURE: 23.5},
|
|
blocking=True,
|
|
)
|
|
mock_pyaxenco_client.set_device_mode.assert_awaited_with("climate1", 8)
|
|
mock_pyaxenco_client.set_device_temperature.assert_awaited_with("climate1", 23.5)
|
|
state = hass.states.get(entity_id)
|
|
assert state is not None
|
|
assert state.attributes["temperature"] == 23.5
|
|
|
|
|
|
async def test_set_preset_mode(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyaxenco_client: AsyncMock,
|
|
) -> None:
|
|
"""Test setting preset mode."""
|
|
mock_pyaxenco_client.get_devices.return_value = [CLIMATE_DEVICE]
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
entity_id = "climate.climate_device"
|
|
await hass.services.async_call(
|
|
"climate",
|
|
"set_preset_mode",
|
|
{ATTR_ENTITY_ID: entity_id, "preset_mode": "eco"},
|
|
blocking=True,
|
|
)
|
|
mock_pyaxenco_client.set_device_mode.assert_awaited_with("climate1", 2)
|
|
state = hass.states.get(entity_id)
|
|
assert state is not None
|
|
assert state.attributes["preset_mode"] == "eco"
|
|
|
|
|
|
async def test_set_hvac_mode(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyaxenco_client: AsyncMock,
|
|
) -> None:
|
|
"""Test setting hvac mode."""
|
|
mock_pyaxenco_client.get_devices.return_value = [CLIMATE_DEVICE]
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
entity_id = "climate.climate_device"
|
|
await hass.services.async_call(
|
|
"climate",
|
|
"set_hvac_mode",
|
|
{ATTR_ENTITY_ID: entity_id, "hvac_mode": "off"},
|
|
blocking=True,
|
|
)
|
|
mock_pyaxenco_client.set_device_mode.assert_awaited_with("climate1", 4)
|
|
state = hass.states.get(entity_id)
|
|
assert state is not None
|
|
assert state.state == "off"
|
|
|
|
|
|
async def test_set_preset_mode_unknown_no_api_call(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyaxenco_client: AsyncMock,
|
|
) -> None:
|
|
"""Unknown preset mode should not call the API and should keep current mode."""
|
|
mock_pyaxenco_client.get_devices.return_value = [CLIMATE_DEVICE]
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
entity_id = "climate.climate_device"
|
|
with pytest.raises(ServiceValidationError):
|
|
await hass.services.async_call(
|
|
"climate",
|
|
"set_preset_mode",
|
|
{ATTR_ENTITY_ID: entity_id, "preset_mode": "not-a-mode"},
|
|
blocking=True,
|
|
)
|
|
|
|
mock_pyaxenco_client.set_device_mode.assert_not_awaited()
|
|
state = hass.states.get(entity_id)
|
|
assert state is not None
|
|
assert state.attributes["preset_mode"] == "comfort"
|
|
|
|
|
|
async def test_set_preset_mode_sub_device_timeout(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyaxenco_client: AsyncMock,
|
|
) -> None:
|
|
"""Sub-device preset mode timeout should raise HomeAssistantError."""
|
|
mock_pyaxenco_client.get_devices.return_value = [CLIMATE_SUB_DEVICE]
|
|
mock_pyaxenco_client.set_sub_device_mode.side_effect = TimeoutError
|
|
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
entity_id = "climate.climate_sub_device"
|
|
with pytest.raises(HomeAssistantError):
|
|
await hass.services.async_call(
|
|
"climate",
|
|
"set_preset_mode",
|
|
{ATTR_ENTITY_ID: entity_id, "preset_mode": "eco"},
|
|
blocking=True,
|
|
)
|
|
|
|
mock_pyaxenco_client.set_sub_device_mode.assert_awaited_with("gw-1", "rfid-1", 2)
|
|
|
|
|
|
async def test_set_temperature_sub_device_missing_parents(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyaxenco_client: AsyncMock,
|
|
) -> None:
|
|
"""Missing parents/rfid should fail temp set without API call."""
|
|
bad_sub = {**CLIMATE_SUB_DEVICE, "parents": {}, "rfid": None}
|
|
mock_pyaxenco_client.get_devices.return_value = [bad_sub]
|
|
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
entity_id = "climate.climate_sub_device"
|
|
with pytest.raises(HomeAssistantError):
|
|
await hass.services.async_call(
|
|
"climate",
|
|
"set_temperature",
|
|
{ATTR_ENTITY_ID: entity_id, ATTR_TEMPERATURE: 21.0},
|
|
blocking=True,
|
|
)
|
|
|
|
mock_pyaxenco_client.set_sub_device_mode.assert_not_awaited()
|
|
mock_pyaxenco_client.set_sub_device_temperature.assert_not_awaited()
|
|
|
|
|
|
async def test_websocket_state_update(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyaxenco_client: AsyncMock,
|
|
) -> None:
|
|
"""Test that entity updates when source data changes via WebSocket."""
|
|
mock_pyaxenco_client.get_devices.return_value = [CLIMATE_DEVICE]
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
entity_id = "climate.climate_device"
|
|
state = hass.states.get(entity_id)
|
|
assert state is not None
|
|
assert state.state == "heat"
|
|
|
|
mock_pyaxenco_client.register_listener.assert_called_once()
|
|
callback = mock_pyaxenco_client.register_listener.call_args[0][1]
|
|
|
|
callback({"currentTemp": 19.0})
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get(entity_id)
|
|
assert state is not None
|
|
assert state.attributes["current_temperature"] == 19.0
|
|
|
|
callback({"targetMode": 2})
|
|
await hass.async_block_till_done()
|
|
state = hass.states.get(entity_id)
|
|
assert state is not None
|
|
assert state.attributes["preset_mode"] == "eco"
|
|
|
|
|
|
async def test_device_becomes_unavailable(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyaxenco_client: AsyncMock,
|
|
) -> None:
|
|
"""Test that entity becomes unavailable when device connection is lost."""
|
|
mock_pyaxenco_client.get_devices.return_value = [CLIMATE_DEVICE]
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
entity_id = "climate.climate_device"
|
|
state = hass.states.get(entity_id)
|
|
assert state is not None
|
|
assert state.state == "heat"
|
|
|
|
callback = mock_pyaxenco_client.register_listener.call_args[0][1]
|
|
|
|
callback({"connected": False})
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get(entity_id)
|
|
assert state is not None
|
|
assert state.state == "unavailable"
|
|
|
|
|
|
async def test_set_temperature_api_error(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyaxenco_client: AsyncMock,
|
|
) -> None:
|
|
"""API errors when setting temperature should be handled gracefully."""
|
|
mock_pyaxenco_client.get_devices.return_value = [CLIMATE_DEVICE]
|
|
mock_pyaxenco_client.set_device_temperature.side_effect = TimeoutError
|
|
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
entity_id = "climate.climate_device"
|
|
with pytest.raises(HomeAssistantError):
|
|
await hass.services.async_call(
|
|
"climate",
|
|
"set_temperature",
|
|
{ATTR_ENTITY_ID: entity_id, ATTR_TEMPERATURE: 23.5},
|
|
blocking=True,
|
|
)
|
|
|
|
state = hass.states.get(entity_id)
|
|
assert state is not None
|
|
assert state.attributes["temperature"] == 22.0
|
|
|
|
|
|
async def test_set_preset_mode_api_error(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyaxenco_client: AsyncMock,
|
|
) -> None:
|
|
"""API errors when setting preset mode should be handled gracefully."""
|
|
mock_pyaxenco_client.get_devices.return_value = [CLIMATE_DEVICE]
|
|
mock_pyaxenco_client.set_device_mode.side_effect = ConnectionError
|
|
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
entity_id = "climate.climate_device"
|
|
with pytest.raises(HomeAssistantError):
|
|
await hass.services.async_call(
|
|
"climate",
|
|
"set_preset_mode",
|
|
{ATTR_ENTITY_ID: entity_id, "preset_mode": "eco"},
|
|
blocking=True,
|
|
)
|
|
|
|
state = hass.states.get(entity_id)
|
|
assert state is not None
|
|
assert state.attributes["preset_mode"] == "comfort"
|
|
|
|
|
|
async def test_set_temperature_sub_device(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyaxenco_client: AsyncMock,
|
|
) -> None:
|
|
"""Test setting temperature for a sub-device uses sub-device API."""
|
|
mock_pyaxenco_client.get_devices.return_value = [CLIMATE_SUB_DEVICE]
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
entity_id = "climate.climate_sub_device"
|
|
await hass.services.async_call(
|
|
"climate",
|
|
"set_temperature",
|
|
{ATTR_ENTITY_ID: entity_id, ATTR_TEMPERATURE: 21.0},
|
|
blocking=True,
|
|
)
|
|
|
|
mock_pyaxenco_client.set_sub_device_mode.assert_awaited_with("gw-1", "rfid-1", 8)
|
|
mock_pyaxenco_client.set_sub_device_temperature.assert_awaited_with(
|
|
"gw-1", "rfid-1", 21.0
|
|
)
|
|
|
|
|
|
async def test_set_preset_mode_sub_device(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyaxenco_client: AsyncMock,
|
|
) -> None:
|
|
"""Test setting preset mode for a sub-device uses sub-device API."""
|
|
mock_pyaxenco_client.get_devices.return_value = [CLIMATE_SUB_DEVICE]
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
entity_id = "climate.climate_sub_device"
|
|
await hass.services.async_call(
|
|
"climate",
|
|
"set_preset_mode",
|
|
{ATTR_ENTITY_ID: entity_id, "preset_mode": "eco"},
|
|
blocking=True,
|
|
)
|
|
|
|
mock_pyaxenco_client.set_sub_device_mode.assert_awaited_with("gw-1", "rfid-1", 2)
|
|
|
|
|
|
async def test_ntd_changeover_sets_cool(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyaxenco_client: AsyncMock,
|
|
) -> None:
|
|
"""NTD with changeOverUser==1 should expose COOL/OFF and start COOL."""
|
|
mock_pyaxenco_client.get_devices.return_value = [CLIMATE_NTD_COOL]
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
entity_id = "climate.climate_ntd"
|
|
state = hass.states.get(entity_id)
|
|
assert state is not None
|
|
assert state.state == "cool"
|
|
assert state.attributes.get("hvac_modes") == [HVACMode.COOL, HVACMode.OFF]
|
|
|
|
|
|
async def test_skip_device_without_id(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyaxenco_client: AsyncMock,
|
|
entity_registry: er.EntityRegistry,
|
|
) -> None:
|
|
"""Devices without _id are skipped during setup."""
|
|
mock_pyaxenco_client.get_devices.return_value = [{"model": "EV30", "name": "NoID"}]
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
entries = er.async_entries_for_config_entry(
|
|
entity_registry, mock_config_entry.entry_id
|
|
)
|
|
assert not any(e.domain == "climate" for e in entries)
|
|
|
|
|
|
async def test_skip_unknown_model(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyaxenco_client: AsyncMock,
|
|
entity_registry: er.EntityRegistry,
|
|
) -> None:
|
|
"""Unknown models are skipped during setup."""
|
|
mock_pyaxenco_client.get_devices.return_value = [
|
|
{
|
|
"_id": "x",
|
|
"name": "Unknown",
|
|
"model": "UNKNOWN",
|
|
"state": {},
|
|
"connected": True,
|
|
}
|
|
]
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
entries = er.async_entries_for_config_entry(
|
|
entity_registry, mock_config_entry.entry_id
|
|
)
|
|
assert not any(e.domain == "climate" for e in entries)
|
|
|
|
|
|
async def test_handle_ws_update_empty_returns(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyaxenco_client: AsyncMock,
|
|
) -> None:
|
|
"""Empty websocket payload should not change climate values."""
|
|
mock_pyaxenco_client.get_devices.return_value = [CLIMATE_DEVICE]
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
entity_id = "climate.climate_device"
|
|
before = hass.states.get(entity_id)
|
|
assert before is not None
|
|
|
|
callback = mock_pyaxenco_client.register_listener.call_args[0][1]
|
|
callback({})
|
|
await hass.async_block_till_done()
|
|
|
|
after = hass.states.get(entity_id)
|
|
assert after is not None
|
|
assert after.state == before.state
|
|
assert (
|
|
after.attributes["current_temperature"]
|
|
== before.attributes["current_temperature"]
|
|
)
|
|
|
|
|
|
async def test_override_temp_update_from_websocket(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyaxenco_client: AsyncMock,
|
|
) -> None:
|
|
"""OverrideTemp websocket update should update target temperature."""
|
|
mock_pyaxenco_client.get_devices.return_value = [CLIMATE_DEVICE]
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
callback = mock_pyaxenco_client.register_listener.call_args[0][1]
|
|
callback({"overrideTemp": 25})
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get("climate.climate_device")
|
|
assert state is not None
|
|
assert state.attributes["temperature"] == 25
|
|
|
|
|
|
async def test_target_temp_update_from_websocket_sub_device(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyaxenco_client: AsyncMock,
|
|
) -> None:
|
|
"""TargetTemp websocket update should update sub-device target temperature."""
|
|
mock_pyaxenco_client.get_devices.return_value = [CLIMATE_SUB_DEVICE]
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
callback = mock_pyaxenco_client.register_listener.call_args[0][1]
|
|
callback({"targetTemp": 22})
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get("climate.climate_sub_device")
|
|
assert state is not None
|
|
assert state.attributes["temperature"] == 22
|
|
|
|
|
|
async def test_targetmode_standby_sets_off(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyaxenco_client: AsyncMock,
|
|
) -> None:
|
|
"""TargetMode of 4 (standby) via websocket should set HVAC mode to OFF."""
|
|
mock_pyaxenco_client.get_devices.return_value = [CLIMATE_DEVICE]
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
callback = mock_pyaxenco_client.register_listener.call_args[0][1]
|
|
callback({"targetMode": 4})
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get("climate.climate_device")
|
|
assert state is not None
|
|
assert state.state == "off"
|
|
|
|
|
|
async def test_changeover_updates_hvac_when_not_off(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyaxenco_client: AsyncMock,
|
|
) -> None:
|
|
"""ChangeOverUser websocket update should switch active NTD mode to cool."""
|
|
ntd_heat = {
|
|
**CLIMATE_NTD_COOL,
|
|
"_id": "climate_ntd_heat",
|
|
"name": "Climate NTD Heat",
|
|
"state": {**CLIMATE_NTD_COOL["state"], "changeOverUser": 0},
|
|
}
|
|
mock_pyaxenco_client.get_devices.return_value = [ntd_heat]
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get("climate.climate_ntd_heat")
|
|
assert state is not None
|
|
assert state.state == "heat"
|
|
|
|
callback = mock_pyaxenco_client.register_listener.call_args[0][1]
|
|
callback({"changeOverUser": 1})
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get("climate.climate_ntd_heat")
|
|
assert state is not None
|
|
assert state.state == "cool"
|
|
|
|
|
|
async def test_set_preset_mode_standby_sets_off(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyaxenco_client: AsyncMock,
|
|
) -> None:
|
|
"""Setting preset mode to standby sets HVAC mode to off."""
|
|
mock_pyaxenco_client.get_devices.return_value = [CLIMATE_DEVICE]
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
entity_id = "climate.climate_device"
|
|
await hass.services.async_call(
|
|
"climate",
|
|
"set_preset_mode",
|
|
{ATTR_ENTITY_ID: entity_id, "preset_mode": "standby"},
|
|
blocking=True,
|
|
)
|
|
|
|
mock_pyaxenco_client.set_device_mode.assert_awaited_with("climate1", 4)
|
|
state = hass.states.get(entity_id)
|
|
assert state is not None
|
|
assert state.state == "off"
|
|
|
|
|
|
async def test_set_preset_mode_when_hvac_off_ntd_sets_cool(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyaxenco_client: AsyncMock,
|
|
) -> None:
|
|
"""Setting a preset on an OFF NTD entity should bring it back to cool."""
|
|
mock_pyaxenco_client.get_devices.return_value = [CLIMATE_NTD_COOL]
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
entity_id = "climate.climate_ntd"
|
|
await hass.services.async_call(
|
|
"climate",
|
|
"set_hvac_mode",
|
|
{ATTR_ENTITY_ID: entity_id, "hvac_mode": HVACMode.OFF},
|
|
blocking=True,
|
|
)
|
|
await hass.services.async_call(
|
|
"climate",
|
|
"set_preset_mode",
|
|
{ATTR_ENTITY_ID: entity_id, "preset_mode": "eco"},
|
|
blocking=True,
|
|
)
|
|
|
|
mock_pyaxenco_client.set_sub_device_mode.assert_has_awaits(
|
|
[call("gw-ntd", "rfid-ntd", 4), call("gw-ntd", "rfid-ntd", 2)]
|
|
)
|
|
state = hass.states.get(entity_id)
|
|
assert state is not None
|
|
assert state.state == "cool"
|
|
|
|
|
|
async def test_set_hvac_mode_restore_fallback_uses_first_non_standby(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyaxenco_client: AsyncMock,
|
|
) -> None:
|
|
"""Restoring from standby should use the first non-standby preset."""
|
|
standby_device = {
|
|
**CLIMATE_DEVICE,
|
|
"_id": "climate_standby",
|
|
"name": "Climate Standby",
|
|
"state": {**CLIMATE_DEVICE["state"], "targetMode": 4},
|
|
}
|
|
mock_pyaxenco_client.get_devices.return_value = [standby_device]
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
entity_id = "climate.climate_standby"
|
|
state = hass.states.get(entity_id)
|
|
assert state is not None
|
|
assert state.state == "off"
|
|
|
|
await hass.services.async_call(
|
|
"climate",
|
|
"set_hvac_mode",
|
|
{ATTR_ENTITY_ID: entity_id, "hvac_mode": HVACMode.HEAT},
|
|
blocking=True,
|
|
)
|
|
|
|
mock_pyaxenco_client.set_device_mode.assert_awaited_with("climate_standby", 8)
|
|
state = hass.states.get(entity_id)
|
|
assert state is not None
|
|
assert state.attributes["preset_mode"] == "setpoint"
|
|
assert state.state == "heat"
|
|
|
|
|
|
async def test_set_hvac_mode_off_api_error(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyaxenco_client: AsyncMock,
|
|
) -> None:
|
|
"""Setting HVAC mode to off that fails at API should raise HomeAssistantError."""
|
|
mock_pyaxenco_client.get_devices.return_value = [CLIMATE_DEVICE]
|
|
mock_pyaxenco_client.set_device_mode.side_effect = TimeoutError
|
|
|
|
mock_config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
entity_id = "climate.climate_device"
|
|
with pytest.raises(HomeAssistantError):
|
|
await hass.services.async_call(
|
|
"climate",
|
|
"set_hvac_mode",
|
|
{ATTR_ENTITY_ID: entity_id, "hvac_mode": "off"},
|
|
blocking=True,
|
|
)
|
|
|
|
state = hass.states.get(entity_id)
|
|
assert state is not None
|
|
assert state.attributes["preset_mode"] == "comfort"
|