petr.polezhaev
8013781e09
ha(H5): README+HACS, скрипт приёмки, релизный чек-лист
...
- custom_components/fglair/README.md: HACS-установка, cloud/ручной/импорт,
превью шаблона, сущности, ключ для ESPHome (reconfigure/CLI), диагностика
и troubleshoot; заглушки screenshots/step-1..8.png (сгенерированы)
- tests/acceptance/test_esphome_ha.py: quick/burst/long через REST API HA
(long-lived token), адаптивная матрица по capabilities, возврат исходного
состояния, CSV-отчёт long, опциональный ESPHome-канал (aioesphomeapi)
- self-test приёмки на моке HA REST (6 тестов, без железа)
- hacs.json; README: раздел Home Assistant; docs/RELEASE_HA.md: публикация
pyfglair на PyPI (wheel/sdist, auditwheel), HACS-релиз, приёмка
- scripts/py-ci.sh: tests/acceptance; план H5 отмечен
2026-09-29 14:42:41 +03:00
petr.polezhaev
43d3ff7dea
ha(H4): исправления по ревью — маскировка ключа в дампе, reload-repair, reconfigure сохраняет поля
...
- major: diagnostics больше не содержит полный lanip_key (esp_home —
маска, entry_data redacted); ключ для ESPHome — через reconfigure/CLI;
план §3 синхронизирован; дамп при незагруженной записи не падает
- reconfigure: сохраняет listen_port/keepalive_ms и текущие overrides/
temp_step; превью-дефолты подхватывают значения записи
- repair fix-flow: подтверждение перезагружает запись (issue снимается
только когда сессия вышла из key_error; иначе создаётся заново)
- key_issue_id helper, использование async_remove_issue (мёртвый код),
CONF_EMAIL/PASSWORD в TO_REDACT, placeholders в fix_flow, тест
плейсхолдеров ru
- тесты обновлены (47 components)
2026-09-29 14:29:36 +03:00
petr.polezhaev
57ee555704
ha(H4): repair key_mismatch, диагностика для ESPHome, reconfigure, переводы
...
- repairs: issue key_mismatch (ERROR, fixable) при состоянии key_error,
ConfirmRepairFlow, снятие issue при online/unload/remove
- config flow: async_step_reconfigure (меню, prefill ручной формы,
unique_id_mismatch, update_reload_and_abort) — путь «перепровижинировать»
- diagnostics: config/esp_home (dsn/lanip_key/key_id/host для secrets.yaml)
+ esp_home_masked и redacted entry_data, runtime-снимок значений
(raw/display) из кэша ядра
- translations: issues.key_mismatch (title/description/fix_flow) en/ru,
abort unique_id_mismatch/reconfigure_successful; тест согласованности
strings↔en↔ru
- FglairClient.events_dropped; mock_ac: параметр key_id в фикстуре
- тесты: diagnostics, repairs issue+fix-flow, reconfigure (успех/чужое
устройство), translations (45 components)
2026-09-29 14:06:39 +03:00
petr.polezhaev
3e0b6b3653
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 отложен (семантика не зафиксирована)
2026-09-29 12:44:34 +03:00
petr.polezhaev
29f62b639c
ha(H2): ниты повторного ревью — pump без параллельных читателей, pending-рефкаунт, точная ошибка свойств
...
- session.py: новый лог-памп не поднимается, пока предыдущий не завершился
(stopping/is_alive), параллельное чтение кольца исключено
- __init__: счётчик pending закрывает окно гонки setup/unload последней
записи; warning при не-словарном fglair: в YAML
- trial: properties_missing — отдельная ошибка формы/abort (en/ru/strings)
- ConfigEntryNotReady: «порт 0» больше не показывается
- тесты: контраст одинакового явного порта, неблокирующий unload (тикер loop)
- план H5: публикация pyfglair именно на PyPI (manylinux x86_64/aarch64)
2026-09-29 12:29:35 +03:00
petr.polezhaev
da01c607a6
ha(H2): исправления по ревью — несколько устройств, suggested values, YAML-import, типы ошибок
...
- critical: listen_port по умолчанию 0 (эфемерный) — несколько ConfigEntry
не конфликтуют; тест двух записей на дефолтном порту
- manifest: добавлен обязательный ключ codeowners
- unload: set_log_handler(None) вынесен в executor (не блокирует loop);
рефкаунт лог-пампа без гонки (старт после успешного setup)
- config flow: возврат введённого в форму при ошибке
(add_suggested_values_to_schema; секреты исключены), YAML-import подключён
через async_setup + SOURCE_IMPORT, ProvisionError.kind вместо разбора
русского текста, валидация device_port/key_id из JSON
- trial: требует получения всех базовых свойств шаблона (не пустой ok),
безопасное создание Session
- py-ci.sh: отсутствие .venv-ha — ошибка (PY_CI_SKIP_HA=1 для пропуска);
pytest.ini testpaths
2026-09-29 12:20:37 +03:00
petr.polezhaev
26d1d6d64f
ha(H2): компонент fglair — manifest, config flow (облако/ручной/импорт), пробная сессия
...
- custom_components/fglair: manifest (config_flow, local_push, requirements),
const, trial (start -> ONLINE <=10 c -> GET базовых свойств), FglairClient
(сессия pyfglair в executor, колбэки в loop, --runtime_data),
__init__ (setup/unload, рефкаунт глобального лог-пампа)
- config flow: меню cloud/manual/import_json + import-source; discover через
pyfglair.provision; пробное подключение с диагностикой (cannot_connect,
no_slots, key_mismatch, timeout, invalid_auth, invalid_import); unique_id=dsn
- strings.json + translations/{en,ru}.json
- тесты HA (pytest-homeassistant-custom-component): меню/шаги/ошибки/импорт/
облако (discover замокан), setup/unload, занятый порт; trial против
mock_ac.py (online/key_mismatch/unreachable/no_slots)
- pytest.ini (общий), tests/conftest перенесён на уровень tests/,
scripts/py-ci.sh: --setup-ha и прогон tests/components
2026-09-29 12:03:44 +03:00
petr.polezhaev
b84bd8d555
ha(H1): исправления по ревью — жизненный цикл сессии, самодостаточный wheel
...
- C1: единственный владелец stop+destroy — reader-поток; close() только
выставляет флаг и ждёт; повторный close и close из колбэка безопасны
(регресс-тесты; TSan на close с мёртвым модулем — 0 гонок)
- M1: wheel собирается с bundled mbedtls (нет внешней soname mbedcrypto);
попутно исправлена bundled-ветка CMake (-lSTATIC)
- C-API: счётчики потерянных событий/логов, лог-кольцо без union-балласта,
атомарные fn/ctx лог-синка
- provision: валидация формата облачных ответов; CLI: человеческие ошибки
(exit 2), --out с правами 0600, console_script pyfglair
- упаковка: MANIFEST.in (sdist с C++-исходниками), очистка stale .so,
readme в метаданных; find_library без ложных .bak
- тесты: close-сценарии, доставка логов, ошибки CLI/provision, C-API очередь
2026-09-29 11:48:32 +03:00
petr.polezhaev
0292404756
ha(H1): pyfglair — cffi-ядро (очередь событий), wheel, CLI discover/monitor
...
- CMake: shared-таргет fgl-aircon-shared (FGL_BUILD_SHARED, PIC, SOVERSION)
- C-API: fgl_prop_raw_range + кольцевые очереди событий/логов
(fgl_session_poll/wait_events, fgl_log_poll/wait_events): прямые колбэки
из потоков ядра со стеком 8 КБ небезопасны для интерпретатора Python
- pyfglair: _corebuild (cmake->wheel), _cffi (ABI), Session (reader-поток ->
loop.call_soon_threadsafe), templates (интроспекция C-таблиц), provision
(aiohttp), CLI discover/monitor + esphome-secrets
- setup.py/pyproject: платформенный wheel py3-none-linux_x86_64 с .so
- tests/pyfglair: templates, provision (мок-облако), session + CLI против
mock_ac.py; scripts/py-ci.sh; ci.sh запускает python-часть
2026-09-29 11:31:57 +03:00
petr.polezhaev
97842276c8
core(M4): полировка по нитам финального ревью
...
- fglair-discover: fchmod 0600 и при перезаписи существующего --out.
- fglctl: #include <cstdlib>; точная проверка обрезки config
(n == sizeof-1 && !feof); валидация 'monitor <sec>' (endptr/errno).
- docs: отчёт soak — формулировка «ответы на batch GET»; индекс reports.
Ревью M4 независимым агентом: 2 круга, APPROVED.
2026-09-29 01:30:30 +03:00
petr.polezhaev
31cd56f082
core(M4): fglctl, fglair-discover, README библиотеки; M5 — вне скоупа core-релиза
...
- README.md (корень): сборка POSIX/ESP-IDF-компонентом, тесты, получение
ключа (fglair-discover), примеры fglctl; planned-компоненты помечены.
- examples/cli/fglctl.cpp: monitor/get/set/status на публичном API
(batch GET шаблона, конверсии, wait_online, логи в stderr, exit-коды;
сборка с -Wall -Wextra -Werror).
- tools/fglair-discover: облачный provisioning (eu/us/cn, endpoints и
app_id/secret из PROTOCOL §7), --format json|esphome-secrets, --out 0600,
обработка URLError/KeyError; stdlib-only.
- tests/tools/test_fglair_discover.py: мок-облако (http.server), 4 теста,
ctest tools_fglair_discover.
- docs/reports/M4_SOAK.md: 60-мин soak на AP-WC1E — 0 рассинхронов,
33/33 свойства (вкл. строковые), 273 опроса модуля, pushes_bad=0;
24-ч прогон — приёмочный шаг перед релизом.
- План: M4 ✅ ; M5 (FglHub/mDNS) — ВНЕ СКОУПА core-релиза.
- Гигиена: config_kata.json (корень+docs/legacy) в .gitignore; MQTT-креды
из docs/legacy/run.sh заменены плейсхолдером (история чистится отдельно).
- Фиксы ревью: mock арм fail-pushes после активации (флейк san-сборки);
fglctl: валидация strtoll (abc/99 — отказ, exit!=0), чтение config с
предупреждением об обрезке, Config{} вместо memset; discover: 0600.
CI: 11/11 ×3 (gcc-Rel, gcc-ASan/UBSan, clang); ESP-IDF esp32 build complete.
2026-09-29 01:16:58 +03:00
petr.polezhaev
fa1718f405
core(M3): aircon-слой — шаблоны A/B/F, конверсии+override, публичный API + C-API
...
- include/fgl-aircon: types.hpp (Prop/State/Error/Value/Template/
Conversion-linear-custom/PropOverride/Config с задокументированными
lifetime-контрактами), templates.hpp (PropInfo-таблицы, интроспекция,
конверсии c контрактом Linear num/den!=0), session.hpp (Session:
set_int/bool/string, get_prop, batch, cached, set_log_sink с контрактом),
c_api.h (extern "C" для cffi/pyfglair).
- src/aircon: tables.cpp (шаблоны A=33/B=20/F=34 по PROTOCOL §8.2,
template_detect + template_is_known; конверсии: default тождественно,
DisplayTemperature raw 0.01°C(+5000)→API 0.1°C; linear с инверсией
raw=(api-offset)*den/num и насыщением __builtin_*_overflow; custom fn;
af_*_swing base_type integer, kind bool), session.cpp (маппинг имя↔Prop,
коэрсинг int-push к kBool по таблице, кэш со спинлоком, оптимистичный кэш,
валидация overrides: терминатор/linear/custom_fn), c_api.cpp (шейм,
static_assert'ы на все enum-значения, fgl_template_detect -1 для
неизвестных).
- ayla: set_property_string (Command.str_value, coalescing копирует строку
в т.ч. в batch-ветке); kMaxQueue 48 + static_assert (полный батч A=33);
commit_batch всегда ставит notify; oversized-строка дропает событие
(кэш не затирается); max_queue default 40.
- tests/aircon: test_tables (составы/атрибуты/дубли/template_detect/
swings-integer), test_convert (default/linear/custom/диапазоны/насыщение
в обе стороны/UB-экстремумы), aircon_runner + test_aircon_mock
(маппинг+конверсии+batch=1-notify с ожиданием async REG; SET+кэш+
RO/не-шаблон отказы+клэмпинг raw 450 на модуле; C-API smoke).
- Прибор AP-WC1E: полный батч шаблона A одним notify — 28 int/bool свойств
(DisplayTemperature 7000→200=20.0°C, DeviceCapabilities=5119), boolean SET
(JSON true) принят, RO/клэмп/NOTMPL отказы корректны.
- CI: 10/10 ×3 стабильно (3 полных прогона); ESP-IDF esp32 build complete.
Ревью независимым агентом: 3 круга — B1 kMaxQueue<батча, B2 linear div/0,
B3 c_api контракт -1, B4 batch-строки, BL1 INT64_MIN negation UB,
BL2 DisplayTemperature экстремумы, flake aircon_mock — всё закрыто;
APPROVED (условие круга 3: фикс + зелёный CI ×3).
2026-09-28 20:16:54 +03:00
petr.polezhaev
17bea87fdf
core(M2): машина состояний сессии Ayla LAN + mock-модуль + интеграционные сценарии
...
- session.{hpp,cpp}: state machine (idle/registering/online/recovering/
offline/key_error); httpd-обработчики key_exchange (200/426/412, re-key
прозрачно), commands (одна команда, 206/200, envelope, глобальный seq_no),
datapoint (unpack -> PropertyEvent / 401+тишина 50с для re-key-восстановления);
сессионный поток: local_reg POST?dsn/PUT (local_ip_for), keep-alive, backoff
x1.6->60с, 503->offline/NoSlot, activation-timeout->recovering, delete_session
с ожиданием выдачи; очередь с coalescing + batch; телеметрия; колбэки из
двух потоков с задокументированным контрактом; буферы datapoint-пути в Impl.
- platform: local_ip_for (UDP-connect) posix+esp-idf; стек httpd 24576
(переполнение 16КБ поймано gdb на Release).
- mock_ac.py: мок-модуль, stdlib-only чистый python AES-256 (свёрстан с
pycryptodome); сценарии: 503, no-poll, rekey-every, stale-gap (эмуляция
'вернувшегося' приложения), fail-pushes (битая подпись), garbage-pushes
(обрыв блока), break-outbound (исходящий десинк -> модуль ре-кает на
local_reg, как probe1-3), push-every, fail-first-ke.
- session_runner + test_session_mock.py: 9 сценариев через ctest, включая
самосинхронизацию CBC и восстановление после исходящего десинка.
- Прибор AP-WC1E: активация <=1с; re-key семантика ИСПРАВЛЕНА по живым
тестам: re-key при зазоре local_reg >= ~44-50с (не по возрасту сессии!);
при честном keep-alive 15с сессия стабильна без re-key; PROTOCOL/LEGACY/
PLAN обновлены; восстановление = тишина >порога + возврат.
- CI: 7/7 x3 (gcc-Rel, gcc-ASan/UBSan, clang); ESP-IDF esp32 build complete.
Ревью под-агентом: 2 круга (стек httpd, залипание состояний, dangling cfg,
физика десинка) — APPROVED.
2026-09-27 14:56:47 +03:00
petr.polezhaev
fe3bda646f
docs: M0, M1 отмечены выполненными в плане библиотеки
2026-09-22 15:46:27 +03:00
petr.polezhaev
e6f6e2d5f4
core(M0): монорепо-каркас — CMake (posix+esp-idf), платслой, лог, мини-httpd
...
- CMakeLists в корне: ветвление ESP_PLATFORM (idf_component_register,
lwip/esp_timer/esp_hw_support/pthread) / POSIX (статическая библиотека
fgl-aircon, C++20, -fno-exceptions -fno-rtti, -Werror).
- src/ayla/platform: сокеты/потоки/CSPRNG/время; posix (getrandom, poll,
pthread_join) и esp-idf (lwip_select, esp_fill_random, pthread-слой IDF);
tcp_shutdown/tcp_local_port/thread_join для управляемой остановки.
- src/ayla: log (sink, без printf); мини-httpd/1.1 (keep-alive, Content-Length,
лимиты заголовков/тела, ephemeral-порт, жизненный цикл с гарантией
завершения потока: shutdown(active)→join→close).
- tests/ayla: platform (join, loopback+shutdown) и httpd (404, keep-alive,
обработчик/парсинг, oversize-400, stop при живом соединении, стрим
заголовков). doctest через FetchContent.
- scripts/ci.sh: сборка+ctest. ESP-IDF v5.5.5 esp32: смоук-сборка с ядром
как компонентом — Project build complete.
Ревью под-агентом: 3 круга, все блокеры (жизненный цикл httpd) закрыты, APPROVED.
2026-09-22 00:27:33 +03:00
petr.polezhaev
301cf5cf70
docs: корректировки планов — монорепо, слои ayla/aircon, конверсии, приёмка
...
- PLAN_CORE: монорепозиторий (CMakeLists в корне; components/fglair,
custom_components/fglair, include/fgl-aircon, src/{ayla,aircon},
src/ayla/platform); логическое разделение ayla (протокол+цикл) /
aircon (конверсии+шаблоны+API); тесты зеркалят слои (tests/{ayla,aircon});
конверсии: шаблон / линейные коэффициенты / функция-указатель
(лямбды ESPHome); оценка httpd/json (jsmn вендор, свой мини-httpd на
BSD-сокетах, conan не нужен); README библиотеки в M4.
- PLAN_ESPHOME: host вместо ip_address (DNS + Ayla-mDNS :10276),
секреты в примерах, кастомные конверсии через !lambda, advanced-пример
(триггеры режимов + LVGL с пропусками), README-план, скрипт приёмки
(aioesphomeapi + HA REST).
- PLAN_HOME_ASSISTANT: шаг config flow с превью рассчитанных значений
шаблона (через cffi в C-ядро, без дублей), README с HACS-инструкцией
и заглушками под скриншоты с описаниями, приёмка (long-lived token).
- Убраны реальные dsn/ip/lanip_key/key_id из примеров; probe_mdns.py
принимает DSN аргументом.
2026-09-21 13:20:29 +03:00
petr.polezhaev
9469f0104c
refactor: legacy и apk перенесены в docs/, дедупликация исходников legacy
...
- docs/legacy/: рабочая раскладка изменённого форка (main.py + пакет
aircon/); плоские дубликаты .py удалены, вложенный .git клона удалён
(это изменённая копия, а не чистый клон), __pycache__ и .gitignore
апстрима убраны. Локальный config_kata.json не версионируется
(содержит lanip_key устройства).
- docs/apk/: манифест APK; бинарники *.apk и icon.png не версионируются.
- Обновлены пути в документации и tools/.
2026-09-17 19:48:55 +03:00
petr.polezhaev
359203abfe
docs: реконструкция LAN-протокола FGLair, анализ legacy, планы fglair-core/HA/ESPHome
...
- PROTOCOL.md: полная спецификация (KDF, envelope/CBC-цепочка, local_reg,
key exchange, commands.json 206/200, datapoint push, тайминги, таблицы
свойств шаблонов A/B/F, облачный provisioning). Факты из APK помечены
[APK], проверенные живыми экспериментами на AP-WC1E — [ПРОВЕРЕНО НА
ПРИБОРЕ]: mDNS только :10276; лимит 2 LAN-сессии (3-я -> 503);
принудительный re-key при возрасте сессии >= ~44с (единственный механизм
самолечения десинхрона — 400/401 модуль игнорирует); записи не
эхируются; delete_session освобождает слот.
- LEGACY_ANALYSIS.md: причины рассинхрона (keep-alive 1200с вместо 10-15с
+ seq_no-фильтр) и перегрузки модуля; требования к новой реализации.
- PLAN_CORE_LIBRARY.md: план C++20-библиотеки fglair-core (Linux + ESP-IDF),
ключ lanip_key считается статичным, ротация — только ошибка + ручной
перепровижининг.
- PLAN_HOME_ASSISTANT.md: pyfglair (cffi wheel) + custom component,
облачный provisioning только в config flow, ключ виден в диагностике
для копирования в ESPHome.
- PLAN_ESPHOME.md: external component, только ESP-IDF framework.
- tools/probe_reference.py: эталонный клиент протокола (проверен на
приборе end-to-end); tools/probe_mdns.py — mDNS-проба.
- legacy/: снимок скрипта (апстрим gyro-labs/AirCon, вложенный клон не
версионируется); apk/*.apk исключены из версионирования.
2026-09-17 19:44:49 +03:00