ha(H3): исправления по ревью — turn_on/off, кламп конверсий, reload, гонка ONLINE
- M1: supported_features += TURN_ON/TURN_OFF (сервисы в HA 2026.9), async_turn_off; удалён мёртвый _enable_turn_on_off_backwards_compat; тест turn_on/turn_off → operation_mode 1/0 - M2: достижимый диапазон = override ∩ raw-таблица шаблона; raw клампится в from_display, clamp() по достижимому диапазону (молчаливый кламп ядра устранён); тесты scaled override - M3: mock_ac сбрасывает crypto/active на DELETE; тест reload → снова ONLINE - гонка: async_sync_now больше не перезаписывает данные устаревшим снимком (ONLINE мог потеряться при быстром старте) — стабильность 4×37 тестов - m1: превью-текст учитывает выбранный в форме шаблон; m2: убрана недостижимая проверка шага; m3: удалён мёртвый translation_key climate - m4: бит 21 op_status (шаблон B); m5: lru_cache template_info/prop_info; m6: operation_mode=1 без истории → hvac_mode unknown; m7: ошибка команды → HomeAssistantError; m8: unload возвращает False, не разбирая runtime - n2: уточнены описания data_description в переводах
This commit is contained in:
@@ -12,7 +12,7 @@ from dataclasses import dataclass
|
||||
from typing import Any, Mapping, Optional
|
||||
|
||||
from pyfglair import Prop, Template
|
||||
from pyfglair.templates import convert_from_input, convert_to_display
|
||||
from pyfglair.templates import convert_from_input, convert_to_display, prop_info
|
||||
|
||||
from .const import (
|
||||
CONF_OVERRIDES,
|
||||
@@ -112,22 +112,61 @@ class ConversionSet:
|
||||
raw = convert_from_input(self.template, prop, api_value)
|
||||
return override.to_display(raw)
|
||||
|
||||
def from_display(self, prop: Prop, display: int) -> int:
|
||||
"""Отображаемое значение → API-значение для set_int()."""
|
||||
def raw_range(self, prop: Prop) -> Optional[tuple[int, int]]:
|
||||
"""raw-диапазон протокольной таблицы шаблона (None — не задан)."""
|
||||
info = prop_info(self.template, prop)
|
||||
if info is None or info.raw_min is None or info.raw_max is None:
|
||||
return None
|
||||
return (min(info.raw_min, info.raw_max), max(info.raw_min, info.raw_max))
|
||||
|
||||
def _display_of_raw(self, prop: Prop, raw: int) -> int:
|
||||
override = self.override(prop)
|
||||
if override.identity:
|
||||
return display
|
||||
raw = override.from_display(display)
|
||||
return convert_to_display(self.template, prop, raw)
|
||||
return override.to_display(raw)
|
||||
|
||||
def from_display(self, prop: Prop, display: int) -> int:
|
||||
"""Отображаемое значение → API-значение для set_int().
|
||||
|
||||
raw клампится по таблице шаблона (ядро тоже клампит — не отдаём
|
||||
молча команду за пределами достижимого).
|
||||
"""
|
||||
override = self.override(prop)
|
||||
raw = display if override.identity else override.from_display(display)
|
||||
raw_range = self.raw_range(prop)
|
||||
if raw_range is not None:
|
||||
raw = max(raw_range[0], min(raw_range[1], raw))
|
||||
if override.identity:
|
||||
return raw
|
||||
return convert_to_display(self.template, prop, raw)
|
||||
|
||||
def clamp(self, prop: Prop, display: int) -> int:
|
||||
return self.override(prop).clamp(display)
|
||||
bounds = self.range(prop)
|
||||
if bounds is None:
|
||||
return display
|
||||
return max(bounds[0], min(bounds[1], display))
|
||||
|
||||
def range(self, prop: Prop) -> Optional[tuple[int, int]]:
|
||||
"""Достижимый диапазон display: override ∩ таблица шаблона."""
|
||||
override = self.override(prop)
|
||||
if override.min_value is None or override.max_value is None:
|
||||
low = override.min_value
|
||||
high = override.max_value
|
||||
if prop == Prop.ADJUST_TEMPERATURE:
|
||||
# Фактические ограничения прибора (PROTOCOL §8.3), если
|
||||
# пользователь не задал свои.
|
||||
low = TEMP_MIN_TENTHS if low is None else low
|
||||
high = TEMP_MAX_TENTHS if high is None else high
|
||||
raw_range = self.raw_range(prop)
|
||||
if raw_range is not None:
|
||||
raw_low = self._display_of_raw(prop, raw_range[0])
|
||||
raw_high = self._display_of_raw(prop, raw_range[1])
|
||||
if raw_low > raw_high:
|
||||
raw_low, raw_high = raw_high, raw_low
|
||||
low = raw_low if low is None else max(low, raw_low)
|
||||
high = raw_high if high is None else min(high, raw_high)
|
||||
if low is None or high is None:
|
||||
return None
|
||||
return (override.min_value, override.max_value)
|
||||
return (low, high)
|
||||
|
||||
def temperature_range(self) -> tuple[int, int]:
|
||||
return self.range(Prop.ADJUST_TEMPERATURE) or (
|
||||
|
||||
Reference in New Issue
Block a user