- close() из другого потока после close из reader-колбэка теперь джойнит reader (гарантия, что destroy завершён к возврату); - предупреждения о переполнении очередей не чаще 1/30 с; - events_dropped сохраняет последнее значение после close; - find_library выбирает .so по версии (а не по длине имени).
fglair-core
Local-control library for Fujitsu General air conditioners (FGLair / Ayla LAN protocol). One C++20 codebase — Linux and ESP-IDF (ESP32) — with the full protocol reconstructed from the official APK and verified on real hardware.
See docs/PROTOCOL.md for the protocol reference and
docs/PLAN_CORE_LIBRARY.md for the design.
Layout
include/fgl-aircon/ public API (types, templates, session, C API for cffi)
src/ayla/ Ayla LAN protocol: crypto, envelope, HTTP, session loop
src/aircon/ property templates A/B/F, conversions, API implementation
pyfglair/ Python bindings (cffi) for Home Assistant (docs/PLAN_HOME_ASSISTANT.md)
components/ ESPHome external component (planned, docs/PLAN_ESPHOME.md)
custom_components/ Home Assistant custom component (planned, docs/PLAN_HOME_ASSISTANT.md)
tests/ ayla (protocol), aircon (conversions), pyfglair, tools
examples/cli/ fglctl — sample CLI
tools/ fglair-discover, protocol probes
Build (Linux / POSIX)
Requirements: CMake ≥ 3.16, Ninja (or Make), g++/clang with C++20, Mbed TLS
(system libmbedcrypto; otherwise it is fetched and built automatically).
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build
# CLI:
./build/fglctl <config.json> status
Build (ESP-IDF component)
The repository root is an ESP-IDF component. Put it on EXTRA_COMPONENT_DIRS
(or copy into your project's components/):
set(EXTRA_COMPONENT_DIRS "/path/to/aircon")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(my_app)
# main/CMakeLists.txt
idf_component_register(SRCS "main.cpp" PRIV_REQUIRES aircon)
#include "fgl-aircon/session.hpp"
extern "C" void app_main() {
fgl::aircon::Config cfg{};
cfg.host = "ac.local"; // DNS name or IP
cfg.dsn = "..."; // from fglair-discover
cfg.lanip_key = "..."; // from fglair-discover (static per module)
cfg.lanip_key_id = 12345;
cfg.tmpl = fgl::aircon::template_detect("AP-WC1E");
fgl::aircon::Callbacks cbs{};
auto* s = fgl::aircon::Session::create(cfg, cbs);
s->start();
}
Tested with ESP-IDF v5.5 (esp32); no Arduino support.
Tests
./scripts/ci.sh # Release + ASan/UBSan + clang builds, all tests
# or:
ctest --test-dir build --output-on-failure
tests/ayla covers the protocol (KDF vectors from the APK, envelope, HTTP,
session scenarios against a Python mock of the module).
tests/aircon covers template tables and conversions.
No hardware needed.
pyfglair (Home Assistant)
pyfglair/ — python-пакет (cffi, ABI-режим) поверх того же C-ядра: сессия с
доставкой событий в asyncio, интроспекция шаблонов и облачный discovery.
pip install . # wheel: cmake собирает libfgl-aircon.so
python -m pyfglair discover --region eu --email you@example.com
python -m pyfglair monitor --config config_home.json --duration 60
Тесты (без железа: mock-модуль + мок-облако):
scripts/py-ci.sh --setup && scripts/py-ci.sh (или pytest tests/pyfglair).
Getting the key (lanip_key)
The key is static (baked into the AC module; only served by the Ayla cloud):
tools/fglair-discover --region eu --email you@example.com
# prints one config JSON per device, or a secrets block:
tools/fglair-discover --region eu --email you@example.com --format esphome-secrets
fglctl
fglctl config.json status # session check + cached temperature
fglctl config.json monitor 60 # follow all events for 60 s
fglctl config.json get OperationMode FanSpeed
fglctl config.json set AdjustTemperature 220 # 22.0 °C (unit 0.1 °C)
Values are in API units: temperatures in 0.1 °C, DisplayTemperature
converted from the module's 0.01 °C+5000 format.
License / provenance
Protocol documentation and code reconstructed from the FGLair 3.4.3 APK for
interoperability; third_party/jsmn is MIT.