"""Test the BryantEvolutionClient type.""" from collections.abc import Generator from datetime import timedelta import logging from unittest.mock import AsyncMock from freezegun.api import FrozenDateTimeFactory import pytest from syrupy.assertion import SnapshotAssertion from homeassistant.components.bryant_evolution.climate import SCAN_INTERVAL from homeassistant.components.climate import ( ATTR_FAN_MODE, ATTR_HVAC_ACTION, ATTR_HVAC_MODE, ATTR_TEMPERATURE, DOMAIN as CLIMATE_DOMAIN, SERVICE_SET_FAN_MODE, SERVICE_SET_HVAC_MODE, SERVICE_SET_TEMPERATURE, HVACAction, ) from homeassistant.const import ATTR_ENTITY_ID from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform _LOGGER = logging.getLogger(__name__) async def trigger_polling(hass: HomeAssistant, freezer: FrozenDateTimeFactory) -> None: """Trigger a polling event.""" freezer.tick(SCAN_INTERVAL + timedelta(seconds=1)) async_fire_time_changed(hass) await hass.async_block_till_done() async def test_setup_integration_success( hass: HomeAssistant, entity_registry: er.EntityRegistry, mock_evolution_entry: MockConfigEntry, snapshot: SnapshotAssertion, ) -> None: """Test that an instance can be constructed.""" await snapshot_platform( hass, entity_registry, snapshot, mock_evolution_entry.entry_id ) async def test_set_temperature_mode_cool( hass: HomeAssistant, mock_evolution_entry: MockConfigEntry, mock_evolution_client_factory: Generator[AsyncMock], freezer: FrozenDateTimeFactory, ) -> None: """Test setting the temperature in cool mode.""" # Start with known initial conditions client = await mock_evolution_client_factory(1, 1, "/dev/unused") client.read_hvac_mode.return_value = ("COOL", False) client.read_cooling_setpoint.return_value = 75 await trigger_polling(hass, freezer) state = hass.states.get("climate.system_1_zone_1") assert state.attributes["temperature"] == 75, state.attributes # Make the call, modifting the mock client to throw an exception on # read to ensure that the update is visible iff we call # async_update_ha_state. data = {ATTR_TEMPERATURE: 70} data[ATTR_ENTITY_ID] = "climate.system_1_zone_1" client.read_cooling_setpoint.side_effect = Exception("fake failure") await hass.services.async_call( CLIMATE_DOMAIN, SERVICE_SET_TEMPERATURE, data, blocking=True ) # Verify effect. client.set_cooling_setpoint.assert_called_once_with(70) state = hass.states.get("climate.system_1_zone_1") assert state.attributes["temperature"] == 70 async def test_set_temperature_mode_heat( hass: HomeAssistant, mock_evolution_entry: MockConfigEntry, mock_evolution_client_factory: Generator[AsyncMock], freezer: FrozenDateTimeFactory, ) -> None: """Test setting the temperature in heat mode.""" # Start with known initial conditions client = await mock_evolution_client_factory(1, 1, "/dev/unused") client.read_hvac_mode.return_value = ("HEAT", False) client.read_heating_setpoint.return_value = 60 await trigger_polling(hass, freezer) # Make the call, modifting the mock client to throw an exception on # read to ensure that the update is visible iff we call # async_update_ha_state. data = {"temperature": 65} data[ATTR_ENTITY_ID] = "climate.system_1_zone_1" client.read_heating_setpoint.side_effect = Exception("fake failure") await hass.services.async_call( CLIMATE_DOMAIN, SERVICE_SET_TEMPERATURE, data, blocking=True ) # Verify effect. state = hass.states.get("climate.system_1_zone_1") assert state.attributes["temperature"] == 65, state.attributes async def test_set_temperature_mode_heat_cool( hass: HomeAssistant, mock_evolution_entry: MockConfigEntry, mock_evolution_client_factory: Generator[AsyncMock], freezer: FrozenDateTimeFactory, ) -> None: """Test setting the temperature in heat_cool mode.""" # Enter heat_cool with known setpoints mock_client = await mock_evolution_client_factory(1, 1, "/dev/unused") mock_client.read_hvac_mode.return_value = ("AUTO", False) mock_client.read_cooling_setpoint.return_value = 90 mock_client.read_heating_setpoint.return_value = 40 await trigger_polling(hass, freezer) state = hass.states.get("climate.system_1_zone_1") assert state.state == "heat_cool" assert state.attributes["target_temp_low"] == 40 assert state.attributes["target_temp_high"] == 90 # Make the call, modifting the mock client to throw an exception on # read to ensure that the update is visible iff we call # async_update_ha_state. mock_client.read_heating_setpoint.side_effect = Exception("fake failure") mock_client.read_cooling_setpoint.side_effect = Exception("fake failure") data = {"target_temp_low": 70, "target_temp_high": 80} data[ATTR_ENTITY_ID] = "climate.system_1_zone_1" await hass.services.async_call( CLIMATE_DOMAIN, SERVICE_SET_TEMPERATURE, data, blocking=True ) state = hass.states.get("climate.system_1_zone_1") assert state.attributes["target_temp_low"] == 70, state.attributes assert state.attributes["target_temp_high"] == 80, state.attributes mock_client.set_cooling_setpoint.assert_called_once_with(80) mock_client.set_heating_setpoint.assert_called_once_with(70) async def test_set_fan_mode( hass: HomeAssistant, mock_evolution_entry: MockConfigEntry, mock_evolution_client_factory: Generator[AsyncMock], ) -> None: """Test that setting fan mode works.""" mock_client = await mock_evolution_client_factory(1, 1, "/dev/unused") fan_modes = ["auto", "low", "med", "high"] for mode in fan_modes: # Make the call, modifting the mock client to throw an exception on # read to ensure that the update is visible iff we call # async_update_ha_state. mock_client.read_fan_mode.side_effect = Exception("fake failure") data = {ATTR_FAN_MODE: mode} data[ATTR_ENTITY_ID] = "climate.system_1_zone_1" await hass.services.async_call( CLIMATE_DOMAIN, SERVICE_SET_FAN_MODE, data, blocking=True ) assert ( hass.states.get("climate.system_1_zone_1").attributes[ATTR_FAN_MODE] == mode ) mock_client.set_fan_mode.assert_called_with(mode) @pytest.mark.parametrize( ("hvac_mode", "evolution_mode"), [("heat_cool", "auto"), ("heat", "heat"), ("cool", "cool"), ("off", "off")], ) async def test_set_hvac_mode( hass: HomeAssistant, mock_evolution_entry: MockConfigEntry, mock_evolution_client_factory: Generator[AsyncMock], hvac_mode, evolution_mode, ) -> None: """Test that setting HVAC mode works.""" mock_client = await mock_evolution_client_factory(1, 1, "/dev/unused") # Make the call, modifting the mock client to throw an exception on # read to ensure that the update is visible iff we call # async_update_ha_state. data = {ATTR_HVAC_MODE: hvac_mode} data[ATTR_ENTITY_ID] = "climate.system_1_zone_1" mock_client.read_hvac_mode.side_effect = Exception("fake failure") await hass.services.async_call( CLIMATE_DOMAIN, SERVICE_SET_HVAC_MODE, data, blocking=True ) await hass.async_block_till_done() assert hass.states.get("climate.system_1_zone_1").state == evolution_mode mock_client.set_hvac_mode.assert_called_with(evolution_mode) @pytest.mark.parametrize( ("curr_temp", "expected_action"), [(62, HVACAction.HEATING), (70, HVACAction.OFF), (80, HVACAction.COOLING)], ) async def test_read_hvac_action_heat_cool( hass: HomeAssistant, mock_evolution_entry: MockConfigEntry, mock_evolution_client_factory: Generator[AsyncMock], freezer: FrozenDateTimeFactory, curr_temp: int, expected_action: HVACAction, ) -> None: """Test that we can read the current HVAC action in heat_cool mode.""" htsp = 68 clsp = 72 mock_client = await mock_evolution_client_factory(1, 1, "/dev/unused") mock_client.read_heating_setpoint.return_value = htsp mock_client.read_cooling_setpoint.return_value = clsp is_active = curr_temp < htsp or curr_temp > clsp mock_client.read_hvac_mode.return_value = ("auto", is_active) mock_client.read_current_temperature.return_value = curr_temp await trigger_polling(hass, freezer) state = hass.states.get("climate.system_1_zone_1") assert state.attributes[ATTR_HVAC_ACTION] == expected_action @pytest.mark.parametrize( ("mode", "active", "expected_action"), [ ("heat", True, "heating"), ("heat", False, "off"), ("cool", True, "cooling"), ("cool", False, "off"), ("off", False, "off"), ], ) async def test_read_hvac_action( hass: HomeAssistant, mock_evolution_entry: MockConfigEntry, mock_evolution_client_factory: Generator[AsyncMock], freezer: FrozenDateTimeFactory, mode: str, active: bool, expected_action: str, ) -> None: """Test that we can read the current HVAC action.""" # Initial state should be no action. assert ( hass.states.get("climate.system_1_zone_1").attributes[ATTR_HVAC_ACTION] == HVACAction.OFF ) # Perturb the system and verify we see an action. mock_client = await mock_evolution_client_factory(1, 1, "/dev/unused") mock_client.read_heating_setpoint.return_value = 75 # Needed if mode == heat mock_client.read_hvac_mode.return_value = (mode, active) await trigger_polling(hass, freezer) assert ( hass.states.get("climate.system_1_zone_1").attributes[ATTR_HVAC_ACTION] == expected_action )