1
0
mirror of https://github.com/home-assistant/core.git synced 2026-07-15 10:34:19 +01:00
Files
core/tests/components/homekit/test_climate_util.py
T

95 lines
3.4 KiB
Python

"""Test the HomeKit climate helper functions."""
import pytest
from homeassistant.components.climate import (
ATTR_FAN_MODES,
ATTR_MAX_TEMP,
ATTR_MIN_TEMP,
ATTR_SWING_MODES,
)
from homeassistant.components.homekit.climate_util import (
HEAT_COOL_DEADBAND,
get_fan_modes_and_speeds,
get_swing_on_mode,
get_temperature_range_from_state,
resolve_target_temp_range,
)
from homeassistant.const import UnitOfTemperature
from homeassistant.core import State
@pytest.mark.parametrize(
("current_high", "current_low", "new_high", "new_low", "expected"),
[
# Ordered pair within bounds is returned unchanged.
(24.0, 20.0, 25.0, None, (25.0, 20.0)),
(24.0, 20.0, None, 21.0, (24.0, 21.0)),
# A narrow but ordered band is preserved; the deadband is not forced.
(24.0, 20.0, None, 23.0, (24.0, 23.0)),
# New high crossing below the low enforces the deadband.
(24.0, 20.0, 18.0, None, (18.0, 18.0 - HEAT_COOL_DEADBAND)),
# New low crossing above the high enforces the deadband.
(20.0, 16.0, None, 22.0, (22.0 + HEAT_COOL_DEADBAND, 22.0)),
# Low dragged to the max: the deadband survives the clamp by moving high.
(20.0, 18.0, None, 30.0, (30.0, 30.0 - HEAT_COOL_DEADBAND)),
# High dragged to the min: the deadband survives by moving high up.
(24.0, 20.0, 7.0, None, (7.0 + HEAT_COOL_DEADBAND, 7.0)),
],
)
def test_resolve_target_temp_range(
current_high: float,
current_low: float,
new_high: float | None,
new_low: float | None,
expected: tuple[float, float],
) -> None:
"""Test the range resolver keeps an ordered, in-bounds pair with a deadband."""
assert (
resolve_target_temp_range(
current_high, current_low, new_high, new_low, 7.0, 30.0
)
== expected
)
@pytest.mark.parametrize(
("attrs", "expected"),
[
# A reported bound of exactly 0 is honored, not treated as missing.
({ATTR_MIN_TEMP: 0, ATTR_MAX_TEMP: 25}, (0.0, 25.0)),
# A missing minimum falls back to the default.
({ATTR_MAX_TEMP: 25}, (7.0, 25.0)),
# A missing maximum falls back to the default.
({ATTR_MIN_TEMP: 10}, (10.0, 35.0)),
# Both missing use both defaults.
({}, (7.0, 35.0)),
],
)
def test_get_temperature_range_from_state(
attrs: dict[str, float], expected: tuple[float, float]
) -> None:
"""Test reported bounds are honored, including an explicit 0, else defaults."""
state = State("climate.test", "cool", attrs)
assert (
get_temperature_range_from_state(state, UnitOfTemperature.CELSIUS, 7.0, 35.0)
== expected
)
def test_get_fan_modes_and_speeds_ignores_non_string() -> None:
"""Test non-string fan modes are ignored rather than raising."""
fan_modes, speeds = get_fan_modes_and_speeds(
{ATTR_FAN_MODES: ["low", None, "high", 3]}
)
assert fan_modes == {"low": "low", "high": "high"}
assert speeds == ["low", "high"]
def test_get_swing_on_mode_none_when_no_swing_modes() -> None:
"""Test the swing helper returns None when the entity has no swing modes."""
assert get_swing_on_mode({}) is None
assert get_swing_on_mode({ATTR_SWING_MODES: []}) is None
assert get_swing_on_mode({ATTR_SWING_MODES: ["custom"]}) is None
assert get_swing_on_mode({ATTR_SWING_MODES: ["off", "vertical"]}) == "vertical"