ha(H3): превью шаблона с конверсиями; climate и сущности

- config flow: шаг preview (шаблон A/B/F, диапазон/шаг уставки, линейные
  коэффициенты num/den/offset, текст-превью по данным trial: hvac/fan/
  swing/capabilities/текущая температура)
- coordinator: push-driven DataUpdateCoordinator (снимок кэша, доступность
  online/recovering, первичный GET-батч, один batch_commit на действие)
- overrides.py: ConversionSet (ручные линейные конверсии и диапазон поверх
  таблиц ядра, применяются в HA)
- entity.py: DeviceInfo/unique_id/доступность, capabilities-фильтры
- climate: hvac/fan/swing/vertical+horizontal swing/preset ECO-BOOST,
  уставка 16-30 °C (шаг по шаблону), current_temperature
- sensor: room_temperature, error_code, connection_state (диагностика)
- binary_sensor: connectivity + флаги op_status (PROTOCOL §8.4)
- switch: 8 флагов-свойств; select: положения заслонок 0..N-1
- тесты: превью/конверсии/ошибки диапазона, сущности против mock_ac
  (снимок, команды, capabilities-фильтр, ручная конверсия), ConversionSet
- mock_ac: --set NAME=VALUE (0b/0x) для сценариев
- план: demand_control отложен (семантика не зафиксирована)
This commit is contained in:
2026-09-29 12:44:34 +03:00
parent 29f62b639c
commit 3e0b6b3653
21 changed files with 1974 additions and 87 deletions
+98 -1
View File
@@ -1,6 +1,9 @@
"""Константы интеграции fglair."""
"""Константы и словари значений интеграции fglair (PROTOCOL §8)."""
from __future__ import annotations
from homeassistant.components.climate import HVACMode
from pyfglair import Prop, State, Template
from pyfglair.provision import REGIONS as _PROVISION_REGIONS
DOMAIN = "fglair"
@@ -19,6 +22,13 @@ CONF_REGION = "region"
CONF_EMAIL = "email"
CONF_PASSWORD = "password"
CONF_CONFIG_JSON = "config_json"
CONF_OVERRIDES = "overrides"
CONF_TEMP_STEP = "temp_step"
CONF_TEMP_MIN = "temp_min"
CONF_TEMP_MAX = "temp_max"
CONF_TEMP_NUM = "temp_num"
CONF_TEMP_DEN = "temp_den"
CONF_TEMP_OFFSET = "temp_offset"
DEFAULT_DEVICE_PORT = 80
# 0 => свободный эфемерный порт: несколько ConfigEntry (несколько
@@ -38,3 +48,90 @@ TRIAL_ERRORS = {
"start_failed": "cannot_connect",
"properties_missing": "properties_missing",
}
# ---------------------------------------------------------------------------
# operation_mode (PROTOCOL §8.3): 0=OFF, 1=ON, 2=AUTO, 3=COOL, 4=DRY,
# 5=FAN, 6=HEAT
# ---------------------------------------------------------------------------
OPERATION_MODE_OFF = 0
OPERATION_MODE_ON = 1
HVAC_MODE_TO_VALUE = {
HVACMode.AUTO: 2,
HVACMode.COOL: 3,
HVACMode.DRY: 4,
HVACMode.FAN_ONLY: 5,
HVACMode.HEAT: 6,
}
HVAC_VALUE_TO_MODE = {value: mode for mode, value in HVAC_MODE_TO_VALUE.items()}
# device_capabilities, биты (PROTOCOL §8.5)
CAP_HVAC = {
HVACMode.AUTO: 4,
HVACMode.COOL: 0,
HVACMode.DRY: 1,
HVACMode.FAN_ONLY: 2,
HVACMode.HEAT: 3,
}
CAP_FAN = {"auto": 5, "high": 6, "medium": 7, "low": 8, "quiet": 9}
CAP_SWING_VERTICAL = 10
CAP_SWING_HORIZONTAL = 11
CAP_ECONOMY = 12
CAP_MIN_HEAT = 13
CAP_INDOOR_FAN_CONTROL = 14
CAP_POWERFUL = 16
CAP_OUTDOOR_LOW_NOISE = 17
CAP_COIL_DRY = 18
# fan_speed (PROTOCOL §8.3)
FAN_VALUE_TO_MODE = {0: "quiet", 1: "low", 2: "medium", 3: "high", 4: "auto"}
FAN_MODE_TO_VALUE = {mode: value for value, mode in FAN_VALUE_TO_MODE.items()}
# preset: ECO = economy_mode, BOOST = powerful_mode
PRESET_ECO = "eco"
PRESET_BOOST = "boost"
PRESET_PROP = {
PRESET_ECO: Prop.ECONOMY_MODE,
PRESET_BOOST: Prop.POWERFUL_MODE,
}
PRESET_CAP = {
PRESET_ECO: CAP_ECONOMY,
PRESET_BOOST: CAP_POWERFUL,
}
SWING_MODES = ["off", "on"]
SWING_HORIZONTAL_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),
}
# op_status, биты (PROTOCOL §8.4): translation_key -> bit
OP_STATUS_BITS: dict[str, int] = {
"defrost": 24,
"maintenance": 22,
"oil_recovery": 28,
"pump_down": 29,
"check_operation": 30,
"simultaneous": 25,
}
# Уставка: фактические ограничения бытового прибора (PROTOCOL §8.3 [LEGACY]);
# единицы API = 0.1 °C.
TEMP_MIN_TENTHS = 160
TEMP_MAX_TENTHS = 300
TEMP_STEP_TENTHS = {
Template.A: 5,
Template.B: 10,
Template.F: 5,
}
# Состояния, в которых сущности доступны (PROTOCOL/план §5).
AVAILABLE_STATES = (State.ONLINE, State.RECOVERING)