Files
fgl-aircon/tests/components/fglair/test_features.py
T
petr.polezhaev 976a2dcbb8 ha(ux): исправления по ревью — быстрый выход пробинга, prefill шаблона, ниты
- major: probe_template прекращает перебор на unreachable/no_slots/key_error
  (недоступный модуль: ~10 c вместо ~49 c), продолжает только при
  properties_missing (доступен, но чужой шаблон); +тест
- m1: extra_state_attributes не падает до добавления в hass
- m2: reconfigure берёт сохранённый шаблон как дефолт (не пересчитывает по
  модели); +тест
- m3: device_capabilities/display_temperature переведены в необязательные
  зонды (trial не падает на молчащем caps; feature-модель трактует None
  как «не режем»)
- m4: async_sync_now(online_timeout) — честное имя/докстринг
- ниты: удалены PRESET_CAP/as_dict/лишние плейсхолдеры, DIRECTION_KEYS
  упрощён, тавтология в тесте исправлена, en-тире
- 59 components + 47 pyfglair/acceptance зелёные
2026-09-29 18:02:52 +03:00

122 lines
4.5 KiB
Python

"""Дефолты feature-модели: device_capabilities, num_dir, presence."""
from __future__ import annotations
from custom_components.fglair.features import (
FeatureSet,
device_default,
resolve_features,
)
from pyfglair import Prop, Template, Value, ValueKind
def _value(value: int) -> Value:
return Value(ValueKind.INT, value)
def test_caps_defaults_ap_wc1e():
values = {Prop.DEVICE_CAPABILITIES: _value(5119)} # 0x13FF
assert device_default("mode_cool", values) is True
assert device_default("mode_heat", values) is True
assert device_default("fan_auto", values) is True
assert device_default("swing_vertical", values) is False
assert device_default("swing_horizontal", values) is False
assert device_default("economy_mode", values) is True
assert device_default("powerful_mode", values) is False
assert device_default("min_heat", values) is False
assert device_default("coil_dry_mode", values) is False
def test_caps_unknown_keeps_everything():
assert device_default("swing_vertical", {}) is True
assert device_default(
"min_heat", {Prop.DEVICE_CAPABILITIES: _value(0)}
) is True
def test_directions_need_num_dir():
zero = {Prop.AF_VERTICAL_NUM_DIR: _value(0)}
five = {Prop.AF_VERTICAL_NUM_DIR: _value(5)}
assert device_default("af_vertical_direction", zero) is False
assert device_default("af_vertical_direction", five) is True
assert device_default("af_vertical_direction", {}) is False
assert device_default("af_horizontal_direction", zero) is False
def test_presence_defaults():
values = {Prop.DISPLAY_TEMPERATURE: _value(200)}
assert device_default("display_temperature", values) is True
assert device_default("wifi_led_enable", values) is False
assert (
device_default(
"display_temperature", {}, {Prop.DISPLAY_TEMPERATURE}
)
is True
)
assert device_default("wifi_led_enable", {}, set()) is False
def test_resolve_and_featureset_override():
values = {Prop.DEVICE_CAPABILITIES: _value(5119)}
resolved = resolve_features(values, set(), {"swing_vertical": True})
assert resolved["swing_vertical"] is True
assert resolved["powerful_mode"] is False
from custom_components.fglair.const import FEATURES
assert len(resolved) == len(FEATURES)
feature_set = FeatureSet({"swing_vertical": True}, values)
assert feature_set.enabled("swing_vertical") is True
assert feature_set.enabled("powerful_mode") is False
default_set = FeatureSet(None, values)
assert default_set.enabled("economy_mode") is True
assert default_set.enabled("swing_vertical") is False
def test_classify_template():
from custom_components.fglair.trial import classify_template
assert classify_template(
{Prop.AF_VERTICAL_MOVE_STEP1}
) is Template.B
assert classify_template(
{Prop.MONITOR1, Prop.AF_VERTICAL_DIRECTION}
) is Template.F
assert classify_template(
{Prop.AF_VERTICAL_DIRECTION}
) is Template.A
assert classify_template({Prop.DISPLAY_TEMPERATURE}) is Template.A
assert classify_template(set()) is None
def test_resolved_features_for_ap_wc1e_without_sensor():
"""Канальный блок: датчик комнаты есть, ламелей/swing нет."""
values = {
Prop.DEVICE_CAPABILITIES: _value(5119),
Prop.AF_VERTICAL_NUM_DIR: _value(0),
Prop.AF_HORIZONTAL_NUM_DIR: _value(0),
Prop.DISPLAY_TEMPERATURE: _value(205),
}
resolved = resolve_features(values, set(values))
assert resolved["display_temperature"] is True
assert resolved["swing_vertical"] is False
assert resolved["af_vertical_direction"] is False
assert resolved["af_horizontal_direction"] is False
assert resolved["mode_cool"] is True
assert resolved["economy_mode"] is True
assert resolved["powerful_mode"] is False
def test_probe_stops_on_unreachable(monkeypatch):
"""Недоступный модуль не перебираем трижды — выходим после первой сессии."""
from custom_components.fglair import trial as tr
calls: list = []
def fake_trial(*args, **kwargs):
calls.append(kwargs.get("template"))
return tr.TrialResult(ok=False, reason="unreachable")
monkeypatch.setattr(tr, "trial_connect", fake_trial)
template, props = tr.probe_template("host", "dsn", "key", 1)
assert template is None
assert props == {}
assert calls == [Template.A]