Files
esphome/tests/integration/test_template_climate_custom_modes.py
T

99 lines
3.5 KiB
Python

"""Integration test for template climate: custom fan modes and presets.
Same on_control (forward) + climate.template.publish (device report, authoritative) pattern as
the enum-based mode/preset fields, but for the custom string variants.
"""
from __future__ import annotations
import asyncio
import aioesphomeapi
from aioesphomeapi import ButtonInfo, ClimateInfo
import pytest
from .host_prefs import clear_host_prefs
from .state_utils import InitialStateHelper, require_entity, wait_for_state
from .types import APIClientConnectedFactory, RunCompiledFunction
DEVICE_NAME = "tmpl-clim-custom"
@pytest.mark.asyncio
async def test_template_climate_custom_modes(
yaml_config: str,
run_compiled: RunCompiledFunction,
api_client_connected: APIClientConnectedFactory,
) -> None:
"""Custom fan mode/preset: traits, on_control forwarding, and publish precedence."""
clear_host_prefs(DEVICE_NAME)
log_lines: list[str] = []
def on_log_line(line: str) -> None:
if "on_control " in line:
log_lines.append(line)
async with (
run_compiled(yaml_config, line_callback=on_log_line),
api_client_connected() as client,
):
async def wait_for_climate_state(
timeout: float = 5.0,
) -> aioesphomeapi.ClimateState:
return await wait_for_state(
client, lambda s: isinstance(s, aioesphomeapi.ClimateState), timeout
)
entities, _ = await client.list_entities_services()
initial_state_helper = InitialStateHelper(entities)
climate_infos = [e for e in entities if isinstance(e, ClimateInfo)]
assert len(climate_infos) == 1, "Expected exactly 1 climate entity"
test_climate = climate_infos[0]
assert set(test_climate.supported_custom_fan_modes) == {
"turbo",
"silent",
"eco",
}
assert set(test_climate.supported_custom_presets) == {
"eco_plus",
"power_save",
"max",
}
report_button = require_entity(entities, "simulate_device_report", ButtonInfo)
client.subscribe_states(
initial_state_helper.on_state_wrapper(lambda state: None)
)
try:
await initial_state_helper.wait_for_initial_states()
except TimeoutError:
pytest.fail("Timeout waiting for initial states")
initial = initial_state_helper.initial_states.get(test_climate.key)
assert initial is not None, "No initial climate state received"
assert isinstance(initial, aioesphomeapi.ClimateState)
assert initial.custom_fan_mode == ""
assert initial.custom_preset == ""
client.climate_command(test_climate.key, custom_fan_mode="turbo")
state = await wait_for_climate_state()
assert state.custom_fan_mode == "turbo"
client.climate_command(test_climate.key, custom_preset="power_save")
state = await wait_for_climate_state()
assert state.custom_preset == "power_save"
await asyncio.sleep(0.2)
assert any("on_control custom_fan_mode=turbo" in line for line in log_lines)
assert any("on_control custom_preset=power_save" in line for line in log_lines)
# The device's report is authoritative and overrides what was commanded above.
client.button_command(report_button.key)
state = await wait_for_climate_state()
assert state.custom_fan_mode == "eco"
assert state.custom_preset == "max"