Files
fgl-aircon/docs/PLAN_HOME_ASSISTANT.md
petr.polezhaev d1c02d7c4b ha(ux): мастер из трёх страниц, feature-модель и capabilities-фильтрация
- config flow: template (A/B/F с пояснениями, значения с устройства,
  определение по oem_model; для неизвестных моделей — пробинг A/B/F тремя
  короткими сессиями) -> capabilities (23 тумблера с описаниями, дефолты
  из device_capabilities/num_dir/ответов) -> limits (диапазон/шаг, ручная
  конверсия); каждая страница с пометкой «значения определены
  автоматически, можно пропустить»
- features.py: FeatureSet/LiveValues/device_default/resolve_features;
  entry.data["features"] хранит выбор пользователя; reconfigure
  предзаполняет; YAML-import получает дефолты
- сущности создаются только для включённых фич: climate (режимы/скорости/
  swing/пресеты), switch, select заслонок (caps+num_dir), датчик комнаты;
  исправлено появление select без ламелей
- trial: caps/num_dir и presence-зонды (необязательные свойства), answered;
  координатор ждёт первые свойства перед созданием сущностей (feature-
  дефолты без гонки)
- атрибут description у сущностей (HA не поддерживает тултипы) + описания
  фич в мастере (en/ru)
- тесты: features, probing, обновлённые flow/entities/repairs; 57 components
2026-09-29 17:39:34 +03:00

12 KiB
Raw Permalink Blame History

План: интеграция Home Assistant (custom_components/fglair)

Аудитория — агенты-реализаторы. Протокол — docs/PROTOCOL.md; ядро — docs/PLAN_CORE_LIBRARY.md (fgl-aircon, C++20, монорепо: библиотека в корне, компонент HA здесь).

1. Архитектура

Home Assistant (custom component `fglair`)
   │  использует python-пакет pyfglair (cffi-bindings к libfgl-aircon.so)
   ▼
pyfglair (wheel: linux x86_64/aarch64; cffi; собирает библиотеку из корня
репозитория через cmake)
   │
   ▼
fgl-aircon (C++, корень монорепо)  ←—— тот же код, что и на ESP32 (ESP-IDF)

Компонент — тонкий: переводит API библиотеки в сущности HA. Вся протокольная логика (сессия, шифрование, pacing, re-key, восстановление) — в C-ядре.

Обоснование: переиспользование библиотеки (требование владельца); cffi-wheel — рабочий путь (HA-контейнеры x86_64/aarch64 debian; cibuildwheel). Fallback, если сборка wheel станет блокером: сборка .so при старте в docker (dev-режим). Чисто-Python повторная реализация протокола — запрещена.

2. Состав

  1. pyfglair (python-пакет):
    • pyfglair/_corebuild.py — cmake-сборка при упаковке wheel;
    • pyfglair/_cffi.py — cffi-декларации поверх include/fgl-aircon/c_api.h;
    • pyfglair/session.py — обёртка Session(cfg); колбэки ядра → asyncio через loop.call_soon_threadsafe. Транспорт событий — C-очередь (fgl_session_wait_events) и выделенный python-поток: прямые cffi-колбэки из потоков ядра небезопасны (у session-потока стек 8 КБ — CPython падает по stack overflow);
    • pyfglair/templates.py — интроспекция шаблонов через cffi-вызовы fgl_template_info_get / fgl_convert_to_display (для превью в config flow; единый источник данных — C-таблицы, дублей нет);
    • pyfglair/provision.py — облачный discovery (перенос логики docs/legacy/aircon/discovery.py на современный aiohttp): e-mail/ пароль/регион → dsn, host/ip, oem_model, lanip_key, lanip_key_id;
    • CLI: python -m pyfglair discover / monitor / --format esphome-secrets (печать блока для secrets.yaml ESPHome).
  2. custom_components/fglair/:
    manifest.json        # requirements: ["pyfglair>=1.0.0"], config_flow: true
    config_flow.py       # flow + options + repair
    fglair_client.py     # фоновый поток с FglSession
    coordinator.py       # push-driven coordinator
    climate.py  sensor.py  switch.py  select.py  binary_sensor.py
    diagnostics.py       # dsn/key_id/host + маска lanip_key; полный ключ — options flow
    translations/{en,ru}.json
    

3. Config flow

Шаг 1 — подключение (один из вариантов):

  • A (облачный): e-mail/пароль FGLair + регион → список устройств (name, model, host) → выбор.
  • B (ручной): host/dsn/lanip_key/lanip_key_id по полям, либо импорт config_*.json (миграция с legacy).

Шаг 2 — пробное подключение: старт сессии, ожидание ONLINE ≤10 с, чтение базовых свойств. Ошибка → назад с сообщением.

Шаг 3 — выбор шаблона С ПРЕВЬЮ РЕЗУЛЬТАТОВ (требование): после выбора шаблона (обычно определён по oem_model автоматически) — форма-предпросмотр рассчитанных значений через pyfglair.templates (вызовы в C-ядро):

Поле превью Пример значения
hvac-режимы off, cool, dry, fan, heat, auto (operation_mode 0–6)
fan-режимы quiet/low/medium/high/auto (0–4)
Диапазон уставки 16.0–30.0 °C, шаг 0.5 (adjust_temperature raw 160–300, ×0.1)
Текущая температура display_temperature 7000 → 20.0 °C
Заслонки vertical 0–4 (af_vertical_num_dir), horizontal 0–6
Битмаска capabilities heat, cool, economy, powerful, min_heat, swing …

Пользователь оценивает корректность (сверяет с приложением FGLair) и подтверждает → создаётся ConfigEntry. При расхождении — возможность выбрать другой шаблон или задать конверсию коэффициентами (linear: num/den/offset) и диапазон вручную на этом же шаге.

Repair (теоретический): key_error → «Ключ устройства изменён — перепровижинируйте» (повторный облако-вход по требованию). Облако в рантайме не используется, ключ статичен.

Диагностика: device-страница показывает dsn, lanip_key_id, host и маску lanip_key (дамп прикладывают в issues — секретов в нём нет). Полный ключ для secrets ESPHome берётся явным действием: reconfigure («Настроить заново») на карточке интеграции или CLI python -m pyfglair discover на хосте HA.

4. Сущности

Как раньше (climate: hvac/fan/swing/preset ECO-BOOST; current_temperature ← display_temperature; sensors: room temp, error_code, op_status-флаги; switch: economy/powerful/coil_dry/min_heat/outdoor_low_noise/ human_det_auto_save/wifi_led/indoor_fan_control; select: заслонки, demand_control; binary_sensor: connectivity). capabilities и выбранные в мастере feature-тумблеры фильтруют сущности (features.py, entry.data["features"]): то, что прибор не умеет или отключено пользователем, не создаётся. Записи — один batch_commit() на действие пользователя. У сущностей есть атрибут description (HA не поддерживает тултипы). Demand_control: семантика значений в PROTOCOL §8.3 не зафиксирована (§10) — сущность не создаётся до уточнения; в H3 вместо неё диагностический sensor состояния связи.

5. Runtime

Один FglairClient на ConfigEntry (daemon-thread, сессия ядра); колбэки → asyncio → push-coordinator. Состояния: online → available; recovering → доступны (последние значения) + diagnostic-сенсор; offline → unavailable; key_error → unavailable + repair. Keep-alive 15 с (самолечение десинхрона ≤15 с). Выгрузка: stop() (delete_session).

6. README компонента (после реализации; для людей, коротко)

Структура (screenshots/step-N.png — заглушки-плейсхолдеры, владелец заменит реальными скриншотами; рядом с каждой — описание что должно быть видно):

  1. Установка через HACS (нужно публичное GitHub-зеркало — HACS не поддерживает GitLab/Gitea; либо ручное копирование custom_components/):
    • HACS → ⋮ → Custom repositories → URL GitHub-зеркала, категория Integration → Add. Скриншот: диалог добавления custom repository с заполненным URL и выбранной категорией Integration.
    • FGLair → Download → перезапуск HA. Скриншот: страница загрузки интеграции с кнопкой Download (версия видна).
  2. Добавление устройства: Settings → Devices & Services → Add Integration → «FGLair». Скриншот: диалог поиска интеграции с введённым «FGLair» и выделенным результатом.
  3. Вход в облако (шаг 1A): e-mail/пароль/регион. Скриншот: форма с заполненными регионом EU и e-mail (пароль скрыт).
  4. Выбор устройства: список найденных кондиционеров. Скриншот: список с одним устройством (имя, модель, host).
  5. Проверка шаблона с превью (шаг 3): Скриншот: форма превью — таблица рассчитанных значений (режимы, диапазон температур, пример конверсии температуры), кнопки Confirm/Change template.
  6. Готово: карточка устройства со списком сущностей. Скриншот: страница устройства с созданными climate/sensor/switch сущностями.
  7. Где взять ключ для ESPHome: диагностика устройства. Скриншот: страница Diagnostics с полями dsn/lanip_key_id/host и маской lanip_key.
  8. Troubleshooting: 503 (оба слота заняты — телефон+ESP?), key_error, недоступность.

7. Приёмка (полуавтоматическая, tests/acceptance/test_esphome_ha.py)

Совместно с ESPHome-компонентом (топология и детали — PLAN_ESPHOME §8). Со стороны HA скрипт использует long-lived access token (профиль → Security → Long-lived access tokens) и REST API: вызов сервисов /api/services/climate/set_temperature|set_hvac_mode|set_fan_mode|set_swing_mode и чтение /api/states/<entity_id>. Возможность подтверждена: это стандартный документированный механизм HA REST API, отдельная авторизация (OAuth-флоу) не нужна — пользователь просто создаёт токен и передаёт скрипту (--ha-url, --ha-token). Режимы: quick (матрица burst/не-burst, обе стороны, с возвратом) и --long (24 ч, ежечасное изменение с проверкой и возвратом, CSV-отчёт дописывается по ходу). Скрипт НЕ открывает собственную сессию к кондиционеру (оба слота заняты HA+ESP). ESPHome-канал (опционально, --esphome-host): через native API отправляются шаги «режим» и «уставка», проверка — по состоянию в HA (сквозная цепочка ESPHome → модуль → HA); fan/swing — только со стороны HA.

8. Этапы

# Содержимое
H1 ✅ wheel pyfglair (сборка из корня монорепо), cffi-обёртки, CLI discover/monitor
H2 ✅ компонент: manifest, config flow (облако/ручной/импорт) + пробное подключение
H3 ✅ шаг «превью шаблона» с ручными конверсиями; climate + сущности
H4 ✅ repair, диагностика (ключ для ESPHome), translations
H5 ✅ README с HACS-инструкцией и заглушками скриншотов (§6), скрипт приёмки (§7), hacs.json; публикация pyfglair на PyPI (manylinux x86_64/aarch64) — релизное действие, чек-лист docs/RELEASE_HA.md