ha(ux): пробинг отдаёт fatal-reason (одна сессия на недоступном модуле); SWITCH_PROPS без битов
This commit is contained in:
@@ -275,7 +275,7 @@ def _run_trial(data: dict[str, Any]) -> tuple[TrialResult, Template, bool]:
|
||||
model = str(data.get(CONF_MODEL) or "")
|
||||
probed = False
|
||||
if not model or detect(model) is None:
|
||||
detected, _probe_props = probe_template(
|
||||
detected, _probe_props, fatal = probe_template(
|
||||
data[CONF_HOST],
|
||||
data[CONF_DSN],
|
||||
data[CONF_LANIP_KEY],
|
||||
@@ -285,6 +285,9 @@ def _run_trial(data: dict[str, Any]) -> tuple[TrialResult, Template, bool]:
|
||||
timeout=TRIAL_TIMEOUT,
|
||||
)
|
||||
probed = True
|
||||
if fatal is not None:
|
||||
# Модуль недоступен/нет слотов/ключ — вторую сессию не открываем.
|
||||
return TrialResult(ok=False, reason=fatal), template, True
|
||||
if detected is not None:
|
||||
template = detected
|
||||
result = trial_connect(
|
||||
|
||||
@@ -96,16 +96,16 @@ PRESET_PROP = {
|
||||
}
|
||||
SWING_MODES = ["off", "on"]
|
||||
|
||||
# switch-свойства: prop -> (translation_key, capability bit | None)
|
||||
SWITCH_PROPS: dict[Prop, tuple[str, int | None]] = {
|
||||
Prop.ECONOMY_MODE: ("economy_mode", CAP_ECONOMY),
|
||||
Prop.POWERFUL_MODE: ("powerful_mode", CAP_POWERFUL),
|
||||
Prop.COIL_DRY_MODE: ("coil_dry_mode", CAP_COIL_DRY),
|
||||
Prop.MIN_HEAT: ("min_heat", CAP_MIN_HEAT),
|
||||
Prop.OUTDOOR_LOW_NOISE: ("outdoor_low_noise", CAP_OUTDOOR_LOW_NOISE),
|
||||
Prop.HUMAN_DET_AUTO_SAVE: ("human_det_auto_save", None),
|
||||
Prop.WIFI_LED_ENABLE: ("wifi_led_enable", None),
|
||||
Prop.INDOOR_FAN_CONTROL: ("indoor_fan_control", CAP_INDOOR_FAN_CONTROL),
|
||||
# switch-свойства: prop -> translation_key (filtracija po FEATURES)
|
||||
SWITCH_PROPS: dict[Prop, str] = {
|
||||
Prop.ECONOMY_MODE: "economy_mode",
|
||||
Prop.POWERFUL_MODE: "powerful_mode",
|
||||
Prop.COIL_DRY_MODE: "coil_dry_mode",
|
||||
Prop.MIN_HEAT: "min_heat",
|
||||
Prop.OUTDOOR_LOW_NOISE: "outdoor_low_noise",
|
||||
Prop.HUMAN_DET_AUTO_SAVE: "human_det_auto_save",
|
||||
Prop.WIFI_LED_ENABLE: "wifi_led_enable",
|
||||
Prop.INDOOR_FAN_CONTROL: "indoor_fan_control",
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -40,7 +40,7 @@ async def async_setup_entry(hass, entry, async_add_entities) -> None:
|
||||
template = runtime.client.template
|
||||
|
||||
entities = []
|
||||
for prop, (key, _capability) in SWITCH_PROPS.items():
|
||||
for prop, key in SWITCH_PROPS.items():
|
||||
if prop_info(template, prop) is None:
|
||||
continue
|
||||
if not coordinator.features.enabled(key):
|
||||
|
||||
@@ -226,11 +226,12 @@ def probe_template(
|
||||
device_port: int = 80,
|
||||
listen_port: int = 10275,
|
||||
timeout: float = 10.0,
|
||||
) -> tuple[Template | None, dict[Prop, Value]]:
|
||||
) -> tuple[Template | None, dict[Prop, Value], str | None]:
|
||||
"""Определяет шаблон по ответам, перебирая A/B/F короткими сессиями.
|
||||
|
||||
Используется только когда oem_model неизвестна ядру: для известных
|
||||
моделей шаблон берётся из C-таблиц без лишних подключений.
|
||||
Возвращает (template, properties, fatal_reason): если модуль недоступен
|
||||
(или нет слотов/ключ не тот), перебор бессмыслен и причина возвращается
|
||||
третьим элементом, чтобы вызывающий не открывал ещё одну сессию.
|
||||
"""
|
||||
answered: set[Prop] = set()
|
||||
properties: dict[Prop, Value] = {}
|
||||
@@ -248,10 +249,10 @@ def probe_template(
|
||||
# Модуль доступен, но свойств шаблона нет — пробуем дальше.
|
||||
continue
|
||||
# Недоступен/нет слотов/ключ — перебор шаблонов бессмыслен.
|
||||
return None, properties
|
||||
return None, properties, result.reason
|
||||
answered |= result.answered
|
||||
properties.update(result.properties)
|
||||
detected = classify_template(answered)
|
||||
if detected is not None:
|
||||
return detected, properties
|
||||
return classify_template(answered), properties
|
||||
return detected, properties, None
|
||||
return classify_template(answered), properties, None
|
||||
|
||||
@@ -580,7 +580,7 @@ async def test_run_trial_probes_unknown_model(hass: HomeAssistant):
|
||||
from custom_components.fglair import config_flow as cf
|
||||
|
||||
with patch.object(
|
||||
cf, "probe_template", return_value=(Template.B, {})
|
||||
cf, "probe_template", return_value=(Template.B, {}, None)
|
||||
) as probe, patch.object(
|
||||
cf, "trial_connect", return_value=TrialResult(ok=True)
|
||||
):
|
||||
|
||||
@@ -115,7 +115,8 @@ def test_probe_stops_on_unreachable(monkeypatch):
|
||||
return tr.TrialResult(ok=False, reason="unreachable")
|
||||
|
||||
monkeypatch.setattr(tr, "trial_connect", fake_trial)
|
||||
template, props = tr.probe_template("host", "dsn", "key", 1)
|
||||
template, props, fatal = tr.probe_template("host", "dsn", "key", 1)
|
||||
assert template is None
|
||||
assert props == {}
|
||||
assert fatal == "unreachable"
|
||||
assert calls == [Template.A]
|
||||
|
||||
Reference in New Issue
Block a user