259 lines
11 KiB
Python
259 lines
11 KiB
Python
"""Константы и словари значений интеграции 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"
|
|
|
|
CONF_HOST = "host"
|
|
CONF_DEVICE_PORT = "device_port"
|
|
CONF_DSN = "dsn"
|
|
CONF_LANIP_KEY = "lanip_key"
|
|
CONF_LANIP_KEY_ID = "lanip_key_id"
|
|
CONF_MODEL = "model"
|
|
CONF_TEMPLATE = "template"
|
|
CONF_NAME = "name"
|
|
CONF_LISTEN_PORT = "listen_port"
|
|
CONF_KEEPALIVE_MS = "keepalive_ms"
|
|
CONF_REGION = "region"
|
|
CONF_EMAIL = "email"
|
|
CONF_PASSWORD = "password"
|
|
CONF_CONFIG_JSON = "config_json"
|
|
CONF_OVERRIDES = "overrides"
|
|
CONF_TEMP_STEP = "temp_step"
|
|
CONF_FEATURES = "features"
|
|
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 (несколько
|
|
# кондиционеров) не конфликтуют; модулю фактический порт сообщается в
|
|
# local_reg (PROTOCOL §1).
|
|
DEFAULT_LISTEN_PORT = 0
|
|
DEFAULT_KEEPALIVE_MS = 15000
|
|
TRIAL_TIMEOUT = 10.0
|
|
|
|
REGIONS = sorted(_PROVISION_REGIONS)
|
|
|
|
TRIAL_ERRORS = {
|
|
"no_slots": "no_slots",
|
|
"key_mismatch": "key_mismatch",
|
|
"unreachable": "cannot_connect",
|
|
"timeout": "timeout",
|
|
"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,
|
|
}
|
|
SWING_MODES = ["off", "on"]
|
|
|
|
# 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",
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Feature-модель (план: страница capabilities в мастере + фильтрация сущностей).
|
|
# key -> (capability bit | None, свойство-источник | None).
|
|
# Для битовых фич дефолт = бит в device_capabilities; для небитовых —
|
|
# наличие свойства в ответах устройства; для заслонок — num_dir > 1.
|
|
# ---------------------------------------------------------------------------
|
|
FEATURES: dict[str, tuple[int | None, Prop | None]] = {
|
|
"mode_cool": (CAP_HVAC[HVACMode.COOL], Prop.OPERATION_MODE),
|
|
"mode_dry": (CAP_HVAC[HVACMode.DRY], Prop.OPERATION_MODE),
|
|
"mode_fan": (CAP_HVAC[HVACMode.FAN_ONLY], Prop.OPERATION_MODE),
|
|
"mode_heat": (CAP_HVAC[HVACMode.HEAT], Prop.OPERATION_MODE),
|
|
"mode_auto": (CAP_HVAC[HVACMode.AUTO], Prop.OPERATION_MODE),
|
|
"fan_quiet": (CAP_FAN["quiet"], Prop.FAN_SPEED),
|
|
"fan_low": (CAP_FAN["low"], Prop.FAN_SPEED),
|
|
"fan_medium": (CAP_FAN["medium"], Prop.FAN_SPEED),
|
|
"fan_high": (CAP_FAN["high"], Prop.FAN_SPEED),
|
|
"fan_auto": (CAP_FAN["auto"], Prop.FAN_SPEED),
|
|
"swing_vertical": (CAP_SWING_VERTICAL, Prop.AF_VERTICAL_SWING),
|
|
"swing_horizontal": (CAP_SWING_HORIZONTAL, Prop.AF_HORIZONTAL_SWING),
|
|
"economy_mode": (CAP_ECONOMY, Prop.ECONOMY_MODE),
|
|
"min_heat": (CAP_MIN_HEAT, Prop.MIN_HEAT),
|
|
"indoor_fan_control": (CAP_INDOOR_FAN_CONTROL, Prop.INDOOR_FAN_CONTROL),
|
|
"powerful_mode": (CAP_POWERFUL, Prop.POWERFUL_MODE),
|
|
"outdoor_low_noise": (CAP_OUTDOOR_LOW_NOISE, Prop.OUTDOOR_LOW_NOISE),
|
|
"coil_dry_mode": (CAP_COIL_DRY, Prop.COIL_DRY_MODE),
|
|
"display_temperature": (None, Prop.DISPLAY_TEMPERATURE),
|
|
"human_det_auto_save": (None, Prop.HUMAN_DET_AUTO_SAVE),
|
|
"wifi_led_enable": (None, Prop.WIFI_LED_ENABLE),
|
|
"af_vertical_direction": (None, Prop.AF_VERTICAL_DIRECTION),
|
|
"af_horizontal_direction": (None, Prop.AF_HORIZONTAL_DIRECTION),
|
|
}
|
|
|
|
# Описания для мастера и для атрибута description сущностей.
|
|
FEATURE_DESCRIPTIONS = {
|
|
"ru": {
|
|
"mode_cool": "Охлаждение",
|
|
"mode_dry": "Осушение",
|
|
"mode_fan": "Вентиляция (без нагрева/охлаждения)",
|
|
"mode_heat": "Обогрев",
|
|
"mode_auto": "Автоматический режим",
|
|
"fan_auto": "Автоскорость вентилятора",
|
|
"fan_quiet": "Тихая скорость",
|
|
"fan_low": "Низкая скорость",
|
|
"fan_medium": "Средняя скорость",
|
|
"fan_high": "Высокая скорость",
|
|
"swing_vertical": "Качание заслонки по вертикали",
|
|
"swing_horizontal": "Качание заслонки по горизонтали",
|
|
"economy_mode": "Экономичный режим",
|
|
"min_heat": "Минимальный обогрев (поддержание +10 °C)",
|
|
"indoor_fan_control": "Управление вентилятором при работе наружного блока",
|
|
"powerful_mode": "Мощный режим (ускоренный выход на режим)",
|
|
"outdoor_low_noise": "Тихий наружный блок",
|
|
"coil_dry_mode": "Сушка испарителя",
|
|
"display_temperature": "Датчик температуры помещения",
|
|
"human_det_auto_save": "Автосохранение при отсутствии людей (датчик присутствия)",
|
|
"wifi_led_enable": "Индикатор Wi-Fi на модуле",
|
|
"af_vertical_direction": "Выбор положения вертикальной заслонки",
|
|
"af_horizontal_direction": "Выбор положения горизонтальной заслонки",
|
|
},
|
|
"en": {
|
|
"mode_cool": "Cooling",
|
|
"mode_dry": "Dry",
|
|
"mode_fan": "Fan only",
|
|
"mode_heat": "Heating",
|
|
"mode_auto": "Automatic mode",
|
|
"fan_auto": "Auto fan speed",
|
|
"fan_quiet": "Quiet fan speed",
|
|
"fan_low": "Low fan speed",
|
|
"fan_medium": "Medium fan speed",
|
|
"fan_high": "High fan speed",
|
|
"swing_vertical": "Vertical louver swing",
|
|
"swing_horizontal": "Horizontal louver swing",
|
|
"economy_mode": "Economy mode",
|
|
"min_heat": "Minimum heat (keep +10 °C)",
|
|
"indoor_fan_control": "Indoor fan control while outdoor unit runs",
|
|
"powerful_mode": "Powerful mode",
|
|
"outdoor_low_noise": "Outdoor unit low noise",
|
|
"coil_dry_mode": "Coil dry",
|
|
"display_temperature": "Room temperature sensor",
|
|
"human_det_auto_save": "Human detection auto save",
|
|
"wifi_led_enable": "Wi-Fi LED indicator",
|
|
"af_vertical_direction": "Vertical louver position",
|
|
"af_horizontal_direction": "Horizontal louver position",
|
|
},
|
|
}
|
|
|
|
ENTITY_DESCRIPTIONS = {
|
|
"ru": {
|
|
"climate": "Управление кондиционером: режим, скорость, уставка, "
|
|
"заслонки, пресеты",
|
|
"room_temperature": "Температура воздуха в помещении",
|
|
"error_code": "Код ошибки кондиционера (0 — ошибок нет)",
|
|
"connection_state": "Состояние связи с модулем кондиционера",
|
|
"connectivity": "Есть ли связь с модулем",
|
|
"defrost": "Идёт разморозка наружного блока",
|
|
"maintenance": "Требуется обслуживание",
|
|
"oil_recovery": "Возврат масла в компрессор",
|
|
"pump_down": "Откачка хладагента (pump down)",
|
|
"check_operation": "Проверочный прогон",
|
|
"simultaneous": "Одновременные операции",
|
|
"different_modes": "Разморозка/масло/прочие режимы (шаблон B)",
|
|
},
|
|
"en": {
|
|
"climate": "Air conditioner control: mode, fan, setpoint, louvers, presets",
|
|
"room_temperature": "Room air temperature",
|
|
"error_code": "Air conditioner error code (0 = no errors)",
|
|
"connection_state": "Connection state with the AC module",
|
|
"connectivity": "Whether the module is reachable",
|
|
"defrost": "Outdoor unit defrost in progress",
|
|
"maintenance": "Maintenance required",
|
|
"oil_recovery": "Compressor oil recovery",
|
|
"pump_down": "Refrigerant pump down",
|
|
"check_operation": "Check operation",
|
|
"simultaneous": "Simultaneous operations",
|
|
"different_modes": "Defrost/oil/other modes (template B)",
|
|
},
|
|
}
|
|
|
|
|
|
def entity_description(language: str, key: str) -> str | None:
|
|
lang = "ru" if str(language).lower().startswith("ru") else "en"
|
|
return ENTITY_DESCRIPTIONS[lang].get(key) or FEATURE_DESCRIPTIONS[lang].get(
|
|
key
|
|
)
|
|
|
|
# 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,
|
|
"different_modes": 21,
|
|
}
|
|
|
|
# Уставка: фактические ограничения бытового прибора (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)
|