Files
2026-08-25 17:51:09 -04:00

201 lines
6.8 KiB
Python

"""Test the Flexit climate entity."""
from unittest.mock import MagicMock, patch
from modbus_connection import ModbusError
from modbus_connection.mock import MockModbusUnit
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.components.climate import (
ATTR_CURRENT_TEMPERATURE,
ATTR_FAN_MODE,
ATTR_HVAC_ACTION,
HVACAction,
)
from homeassistant.components.flexit.climate import async_setup_platform
from homeassistant.components.flexit.const import DOMAIN
from homeassistant.const import ATTR_TEMPERATURE
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import (
device_registry as dr,
entity_registry as er,
issue_registry as ir,
)
from tests.common import MockConfigEntry, snapshot_platform
from tests.components.climate.common import async_set_fan_mode, async_set_temperature
CLIMATE_ENTITY_ID = "climate.flexit"
# A plausible, "healthy" set of register values.
DEFAULT_HOLDING: dict[int, int] = {
8: 215, # target_temperature -> 21.5
17: 2, # fan_mode -> Medium
}
DEFAULT_INPUT: dict[int, int] = {
9: 200, # supply_air_temperature -> 20.0
11: 50, # outdoor_air_temperature -> 5.0
8: 120, # filter_running_hours
14: 0, # heat_exchanger_regulation
15: 0, # electric_heater_regulation
13: 0, # cooling_regulation
27: 0, # filter_alarm
28: 0, # electric_heater_enabled
48: 0, # actual_air_speed
}
@pytest.fixture(autouse=True)
def _load_default_registers(mock_modbus_unit: MockModbusUnit) -> None:
"""Populate the mock unit with a healthy default register set."""
mock_modbus_unit.holding.update(DEFAULT_HOLDING)
mock_modbus_unit.input.update(DEFAULT_INPUT)
async def _setup_integration(
hass: HomeAssistant, config_entry: MockConfigEntry
) -> None:
"""Set up the integration."""
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
async def test_deprecated_yaml_issue(
hass: HomeAssistant, issue_registry: ir.IssueRegistry
) -> None:
"""Test deprecated YAML remains functional and creates a repair issue."""
async_add_entities = MagicMock()
with patch("homeassistant.components.flexit.climate.get_hub") as get_hub:
await async_setup_platform(
hass,
{"hub": "modbus_hub", "slave": 1, "name": "Flexit"},
async_add_entities,
)
issue = issue_registry.async_get_issue(DOMAIN, "deprecated_yaml_no_import")
assert issue is not None
assert issue.breaks_in_ha_version == "2027.3.0"
get_hub.assert_called_once_with(hass, "modbus_hub")
async_add_entities.assert_called_once()
async def test_climate_entity(
hass: HomeAssistant,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
device_registry: dr.DeviceRegistry,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test climate entity setup and state."""
await _setup_integration(hass, mock_config_entry)
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
device_entry = device_registry.async_get_device_by_identifier(
("flexit", mock_config_entry.entry_id), mock_config_entry.entry_id
)
assert device_entry
assert device_entry.configuration_url is None
entity_entries = er.async_entries_for_config_entry(
entity_registry, mock_config_entry.entry_id
)
for entity_entry in entity_entries:
assert entity_entry.device_id == device_entry.id
async def test_climate_entity_state(
hass: HomeAssistant, mock_config_entry: MockConfigEntry
) -> None:
"""Test the climate entity reads temperatures and fan mode from registers."""
await _setup_integration(hass, mock_config_entry)
state = hass.states.get(CLIMATE_ENTITY_ID)
assert state is not None
assert state.attributes[ATTR_TEMPERATURE] == 21.5
assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 20.0
assert state.attributes[ATTR_FAN_MODE] == "Medium"
@pytest.mark.parametrize(
("heating", "cooling", "heat_recovery", "air_speed", "expected_action"),
[
(10, 0, 0, 0, HVACAction.HEATING),
(0, 10, 0, 0, HVACAction.COOLING),
(0, 0, 10, 0, HVACAction.IDLE),
(0, 0, 0, 10, HVACAction.FAN),
(0, 0, 0, 0, HVACAction.OFF),
],
)
async def test_climate_entity_hvac_action(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_modbus_unit: MockModbusUnit,
heating: int,
cooling: int,
heat_recovery: int,
air_speed: int,
expected_action: HVACAction,
) -> None:
"""Test hvac_action resolves based on heating/cooling/recovery/air speed."""
mock_modbus_unit.input[15] = heating
mock_modbus_unit.input[13] = cooling
mock_modbus_unit.input[14] = heat_recovery
mock_modbus_unit.input[48] = air_speed
await _setup_integration(hass, mock_config_entry)
assert hass.states.get(CLIMATE_ENTITY_ID).attributes[ATTR_HVAC_ACTION] is (
expected_action
)
async def test_climate_entity_set_temperature(
hass: HomeAssistant, mock_config_entry: MockConfigEntry
) -> None:
"""Test setting a valid target temperature writes to the register."""
await _setup_integration(hass, mock_config_entry)
await async_set_temperature(hass, temperature=22.5, entity_id=CLIMATE_ENTITY_ID)
assert hass.states.get(CLIMATE_ENTITY_ID).attributes[ATTR_TEMPERATURE] == 22.5
async def test_climate_entity_set_temperature_handles_modbus_error(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_modbus_unit: MockModbusUnit,
) -> None:
"""Test setting target temperature handles Modbus write errors."""
await _setup_integration(hass, mock_config_entry)
mock_modbus_unit.fail_write(8, ModbusError("write failed"))
with pytest.raises(HomeAssistantError):
await async_set_temperature(hass, temperature=22.5, entity_id=CLIMATE_ENTITY_ID)
async def test_climate_entity_set_fan_mode(
hass: HomeAssistant, mock_config_entry: MockConfigEntry
) -> None:
"""Test setting a valid fan mode writes the correct value to the register."""
await _setup_integration(hass, mock_config_entry)
await async_set_fan_mode(hass, "High", entity_id=CLIMATE_ENTITY_ID)
assert hass.states.get(CLIMATE_ENTITY_ID).attributes[ATTR_FAN_MODE] == "High"
async def test_climate_entity_set_fan_mode_handles_modbus_error(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_modbus_unit: MockModbusUnit,
) -> None:
"""Test setting fan mode handles Modbus write errors."""
await _setup_integration(hass, mock_config_entry)
mock_modbus_unit.fail_write(17, ModbusError("write failed"))
with pytest.raises(HomeAssistantError):
await async_set_fan_mode(hass, "High", entity_id=CLIMATE_ENTITY_ID)