mirror of
https://github.com/home-assistant/core.git
synced 2026-07-15 10:34:19 +01:00
95 lines
3.4 KiB
Python
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"
|