ha(H5): исправления по ревью — GitHub/HACS, рабочий ESPHome-канал, restore, manylinux

- critical: README честно описывает HACS (только публичный GitHub-зеркало)
  + ручная установка копированием; RELEASE_HA — шаг зеркала и codeowners
- critical: ESPHome-канал приёмки реализован реально (aioesphomeapi:
  connect/list_entities/climate_command для режима и уставки; fan/swing —
  только HA); unit-тест на фейковом модуле
- major: quick/long восстанавливают исходное состояние в finally даже при
  сбое; quick ждёт восстановления; ошибки шага пишутся в результат (rc 1),
  а не фаталят (rc 2)
- major: RELEASE_HA — manylinux через auditwheel repair (PyPI отклоняет
  linux_x86_64), build/twine, aarch64, корректные проверки .so, тег/версия
- major: полный ключ для ESPHome теперь реально виден в options flow
  («Настройка» на карточке интеграции, поле LAN IP key, можно заменить) —
  README/diagnostics синхронизированы
- minor: non-JSON ответ → rc 2; CSV quick+long и инкрементальная запись;
  допуск _matches в long; --settle-timeout; hacs.json homeassistant=2025.1;
  brand/{icon,logo}.png заглушки; план §6/§7 уточнён
- тесты: acceptance self-test 11 (restore-after-failure, long fail, non-JSON,
  esphome channel), options flow (2), всего 28+11+49
This commit is contained in:
2026-09-29 14:54:50 +03:00
parent 8013781e09
commit a03458f9bc
15 changed files with 609 additions and 178 deletions
+22 -6
View File
@@ -17,9 +17,15 @@
* Модуль кондиционера в той же LAN. Модуль поддерживает **2 LAN-сессии**:
телефон с FGLair и HA уживаются; третья (например, ESPHome) получит 503.
## Установка через HACS
## Установка
1. HACS → ⋮ → **Custom repositories** → URL репозитория, категория
### Вариант A: HACS (нужно GitHub-зеркало)
HACS работает только с публичными репозиториями на **GitHub**; GitLab/Gitea
(включая текущий хостинг проекта) напрямую не поддерживаются. Зеркалируйте
репозиторий на GitHub и добавьте зеркало в HACS:
1. HACS → ⋮ → **Custom repositories** → URL GitHub-зеркала, категория
**Integration** → **Add**.
![шаг 1](screenshots/step-1.png)
@@ -34,6 +40,13 @@
*Должно быть видно: страница загрузки интеграции с кнопкой Download и
версией.*
### Вариант B: вручную (любой хостинг)
Скопируйте каталог `custom_components/fglair/` в
`<config>/custom_components/fglair/` вашего HA и перезапустите Home
Assistant. Убедитесь, что `pyfglair` установлен в python-окружение HA
(см. «Требования»).
## Добавление устройства
3. Settings → Devices & Services → **Add Integration** → «FGLair».
@@ -43,7 +56,7 @@
*Должно быть видно: диалог поиска интеграции с введённым «FGLair» и
выделенным результатом.*
4. **Вход в облако** (шаг 1A): e-mail/пароль FGLair и регион. Облако
4. **Вход в облако**: e-mail/пароль FGLair и регион. Облако
используется один раз — получить статический LAN-ключ модуля.
![шаг 4](screenshots/step-4.png)
@@ -57,7 +70,7 @@
*Должно быть видно: список с одним устройством (имя, модель, host).*
6. **Проверка шаблона с превью** (шаг 3): форма-предпросмотр рассчитанных
6. **Проверка шаблона с превью**: форма-предпросмотр рассчитанных
значений (режимы, диапазон температур, пример конверсии) и, при
необходимости, ручные коэффициенты/диапазон уставки. Сверьте с
приложением FGLair и подтвердите.
@@ -80,8 +93,11 @@ Download diagnostics на странице устройства показыва
`lanip_key_id`, `host` и **маску** ключа. Полный ключ (для
`secrets.yaml` ESPHome) получите явным действием:
* **Настроить заново** (Reconfigure) на карточке интеграции — вход в
облако FGLair покажет/сохранит актуальный ключ; либо
* **Настройка** (шестерёнка → Configure) на карточке интеграции: поле
«LAN IP key» содержит полный ключ — скопируйте его (там же можно
заменить ключ без облака); либо
* **Настроить заново** (Reconfigure) — повторный вход в облако FGLair
обновит ключ; либо
* `python -m pyfglair discover --region eu --email you@example.com
--format esphome-secrets` на хосте HA.
Binary file not shown.

After

Width:  |  Height:  |  Size: 607 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 832 B

+43 -1
View File
@@ -8,7 +8,12 @@ from functools import partial
from typing import Any
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry, ConfigFlow, ConfigFlowResult
from homeassistant.config_entries import (
ConfigEntry,
ConfigFlow,
ConfigFlowResult,
OptionsFlow,
)
from homeassistant.helpers import selector
import pyfglair
@@ -547,3 +552,40 @@ class FglairConfigFlow(ConfigFlow, domain=DOMAIN):
self._reconfigure_entry, title=title, data=data
)
return self.async_create_entry(title=title, data=data)
@staticmethod
def async_get_options_flow(config_entry: ConfigEntry) -> "FglairOptionsFlow":
return FglairOptionsFlow()
class FglairOptionsFlow(OptionsFlow):
"""Показывает полный LAN-ключ (для secrets.yaml ESPHome) и позволяет
обновить его без облака."""
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
entry = self.config_entry
if user_input is not None:
key = str(user_input[CONF_LANIP_KEY]).strip()
if key and key != entry.data.get(CONF_LANIP_KEY):
self.hass.config_entries.async_update_entry(
entry, data={**entry.data, CONF_LANIP_KEY: key}
)
self.hass.config_entries.async_schedule_reload(entry.entry_id)
return self.async_create_entry(title="", data={})
schema = vol.Schema(
{vol.Required(CONF_LANIP_KEY): selector.TextSelector()}
)
schema = self.add_suggested_values_to_schema(
schema, {CONF_LANIP_KEY: entry.data.get(CONF_LANIP_KEY, "")}
)
return self.async_show_form(
step_id="init",
data_schema=schema,
description_placeholders={
"dsn": str(entry.data.get(CONF_DSN, "")),
"lanip_key_id": str(entry.data.get(CONF_LANIP_KEY_ID, "")),
"host": str(entry.data.get(CONF_HOST, "")),
},
)
+2 -1
View File
@@ -2,7 +2,8 @@
Дамп redacted: lanip_key маскируется (HA-конвенция; дампы прикладывают в
issues). Полный ключ для secrets.yaml ESPHome берётся явным действием:
«Настроить заново» (reconfigure) на карточке интеграции или CLI
«Настройка» (шестерёнка, options flow) на карточке интеграции —
одноимённое поле содержит полный ключ; либо CLI
``python -m pyfglair discover`` на хосте HA.
"""
from __future__ import annotations
+11
View File
@@ -189,5 +189,16 @@
}
}
}
},
"options": {
"step": {
"init": {
"title": "FGLair LAN key",
"description": "Full LAN key (for ESPHome secrets.yaml). Copy it, or paste a new one to replace the stored key. DSN: {dsn}; key ID: {lanip_key_id}; host: {host}. Saving a changed key reloads the device.",
"data": {
"lanip_key": "LAN IP key"
}
}
}
}
}
@@ -189,5 +189,16 @@
}
}
}
},
"options": {
"step": {
"init": {
"title": "FGLair LAN key",
"description": "Full LAN key (for ESPHome secrets.yaml). Copy it, or paste a new one to replace the stored key. DSN: {dsn}; key ID: {lanip_key_id}; host: {host}. Saving a changed key reloads the device.",
"data": {
"lanip_key": "LAN IP key"
}
}
}
}
}
@@ -189,5 +189,16 @@
}
}
}
},
"options": {
"step": {
"init": {
"title": "LAN-ключ FGLair",
"description": "Полный LAN-ключ (для secrets.yaml ESPHome). Скопируйте его или вставьте новый, чтобы заменить сохранённый. DSN: {dsn}; key ID: {lanip_key_id}; host: {host}. Сохранение изменённого ключа перезагружает устройство.",
"data": {
"lanip_key": "LAN IP key"
}
}
}
}
}