Files
fgl-aircon/include/fgl-aircon/c_api.h
T
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

219 lines
8.1 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// C-API (extern "C") fgl-aircon — для cffi/pyfglair (Home Assistant).
// C++-пользователям — fgl-aircon/*.hpp.
#pragma once
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
// Версия библиотеки.
const char* fgl_version(void);
// Логирование. Логи ядра всегда пишутся в кольцевой буфер (см.
// fgl_log_wait_events) — sink лишь дополнительный прямой колбэк. Прямой
// колбэк вызывается из потоков ядра с малым стеком (session-поток — 8 КБ):
// вызывать из него Python нельзя; для cffi используйте очередь.
typedef void (*fgl_log_sink_fn)(int level, const char* msg, unsigned len,
void* ctx);
void fgl_log_set_sink(fgl_log_sink_fn sink, void* ctx);
void fgl_log_set_level(int min_level);
// Состояния/ошибки (значения совпадают с fgl::aircon::State/Error).
typedef enum {
FGL_STATE_IDLE = 0,
FGL_STATE_REGISTERING = 1,
FGL_STATE_ONLINE = 2,
FGL_STATE_RECOVERING = 3,
FGL_STATE_OFFLINE = 4,
FGL_STATE_KEY_ERROR = 5,
} fgl_state_t;
typedef enum {
FGL_ERR_NONE = 0,
FGL_ERR_NO_SLOT = 1,
FGL_ERR_UNREACHABLE = 2,
FGL_ERR_KEY_MISMATCH = 3,
FGL_ERR_BAD_KEY_EXCHANGE = 4,
FGL_ERR_ACTIVATION_TIMEOUT = 5,
FGL_ERR_DECRYPT_FAILED = 6,
} fgl_error_t;
typedef enum {
FGL_PROP_OPERATION_MODE = 0,
FGL_PROP_FAN_SPEED = 1,
FGL_PROP_ADJUST_TEMPERATURE = 2,
FGL_PROP_DISPLAY_TEMPERATURE = 3,
FGL_PROP_AF_VERTICAL_DIRECTION = 4,
FGL_PROP_AF_VERTICAL_SWING = 5,
FGL_PROP_AF_HORIZONTAL_DIRECTION = 6,
FGL_PROP_AF_HORIZONTAL_SWING = 7,
FGL_PROP_AF_VERTICAL_MOVE_STEP1 = 8,
FGL_PROP_AF_HORIZONTAL_MOVE_STEP1 = 9,
FGL_PROP_OUTDOOR_LOW_NOISE = 10,
FGL_PROP_INDOOR_FAN_CONTROL = 11,
FGL_PROP_HUMAN_DET_AUTO_SAVE = 12,
FGL_PROP_MIN_HEAT = 13,
FGL_PROP_POWERFUL_MODE = 14,
FGL_PROP_COIL_DRY_MODE = 15,
FGL_PROP_ECONOMY_MODE = 16,
FGL_PROP_MASTER_TIMER_ON_OFF_1 = 17,
FGL_PROP_MASTER_TIMER_ON_OFF_2 = 18,
FGL_PROP_ERROR_CODE = 19,
FGL_PROP_DEMAND_CONTROL = 20,
FGL_PROP_FILTER_SIGN_RESET_DISPLAY = 21,
FGL_PROP_FILTER_SIGN_RESET = 22,
FGL_PROP_OP_STATUS = 23,
FGL_PROP_DEVICE_NAME = 24,
FGL_PROP_BUILDING_NAME = 25,
FGL_PROP_WIFI_LED_ENABLE = 26,
FGL_PROP_SERVICE_CONTACT_NAME = 27,
FGL_PROP_SERVICE_CONTACT_PHONE = 28,
FGL_PROP_SERVICE_CONTACT_EMAIL = 29,
FGL_PROP_AF_HORIZONTAL_NUM_DIR = 30,
FGL_PROP_AF_VERTICAL_NUM_DIR = 31,
FGL_PROP_DEVICE_CAPABILITIES = 32,
FGL_PROP_GET_PROP = 33,
FGL_PROP_HUMAN_DET = 34,
FGL_PROP_MONITOR1 = 35,
FGL_PROP_REFRESH = 36,
FGL_PROP_COUNT = 37,
} fgl_prop_t;
typedef enum {
FGL_VALUE_INT = 0,
FGL_VALUE_BOOL = 1,
FGL_VALUE_STRING = 2,
} fgl_value_kind_t;
typedef struct {
fgl_value_kind_t kind;
int64_t i;
char s[64];
} fgl_value_t;
typedef enum {
FGL_TEMPLATE_A = 0,
FGL_TEMPLATE_B = 1,
FGL_TEMPLATE_F = 2,
} fgl_template_t;
// Конфигурация. Строки копируются при создании.
typedef struct {
const char* host;
uint16_t device_port;
const char* dsn;
const char* lanip_key;
uint32_t lanip_key_id;
fgl_template_t tmpl;
uint16_t listen_port;
uint32_t keepalive_ms;
uint8_t max_queue;
} fgl_config_t;
typedef struct {
fgl_prop_t prop;
int32_t cmd_id; // -1 — спонтанное обновление
int32_t status;
fgl_value_t value;
} fgl_property_event_t;
typedef void (*fgl_on_state_fn)(void* ctx, fgl_state_t st, fgl_error_t err);
typedef void (*fgl_on_property_fn)(void* ctx,
const fgl_property_event_t* ev);
typedef struct {
fgl_on_state_fn on_state;
fgl_on_property_fn on_property;
void* ctx;
} fgl_callbacks_t;
// Непрозрачная сессия (определение — в реализации).
typedef struct fgl_session fgl_session_t;
// ---------------------------------------------------------------------------
// Очередь событий (опросный транспорт для cffi/pyfglair).
// КОНТРАКТ: колбэки fgl_callbacks_t вызываются из потоков ядра с малым стеком
// (session-поток — 8 КБ), поэтому вызывать из них интерпретатор Python
// небезопасно. Вместо этого ядро складывает события/логи в ограниченные
// кольцевые буферы, а python-поток забирает их fgl_session_wait_events /
// fgl_log_wait_events. Колбэки fgl_callbacks_t при этом продолжают работать
// (для C/C++ потребителей).
// ---------------------------------------------------------------------------
typedef enum {
FGL_EVENT_STATE = 0,
FGL_EVENT_PROPERTY = 1,
} fgl_event_type_t;
typedef struct {
int level;
char msg[160];
} fgl_log_event_t;
typedef struct {
fgl_event_type_t type;
union {
struct {
fgl_state_t state;
fgl_error_t error;
} state;
fgl_property_event_t property;
} data;
} fgl_event_t;
// Неблокирующая выборка событий; возвращает число заполненных out
// (0..max_events). При переполнении буфера самые старые события теряются.
int fgl_session_poll_events(fgl_session_t* s, fgl_event_t* out, int max_events);
// Блокирующая выборка: ждёт событие/таймаут (timeout_ms; < 0 — бесконечно).
// Возвращает число заполненных out.
int fgl_session_wait_events(fgl_session_t* s, fgl_event_t* out, int max_events,
int timeout_ms);
// Счётчик потерянных (переполнение кольца) событий сессии/логов.
uint64_t fgl_session_events_dropped(const fgl_session_t* s);
uint64_t fgl_log_events_dropped(void);
// Логи ядра (общий кольцевой буфер; включается при создании C-API-сессии).
int fgl_log_poll_events(fgl_log_event_t* out, int max_events);
int fgl_log_wait_events(fgl_log_event_t* out, int max_events, int timeout_ms);
fgl_session_t* fgl_session_create(const fgl_config_t* cfg,
const fgl_callbacks_t* cbs);
void fgl_session_destroy(fgl_session_t* s);
int fgl_session_start(fgl_session_t* s); // 1 ok / 0 ошибка
void fgl_session_stop(fgl_session_t* s);
fgl_state_t fgl_session_state(const fgl_session_t* s);
fgl_error_t fgl_session_last_error(const fgl_session_t* s);
int fgl_session_set_int(fgl_session_t* s, fgl_prop_t prop, int64_t value);
int fgl_session_set_bool(fgl_session_t* s, fgl_prop_t prop, int value);
int fgl_session_set_string(fgl_session_t* s, fgl_prop_t prop,
const char* value);
int fgl_session_get_prop(fgl_session_t* s, fgl_prop_t prop);
int fgl_session_batch_begin(fgl_session_t* s);
int fgl_session_batch_commit(fgl_session_t* s);
int fgl_session_batch_abort(fgl_session_t* s);
int fgl_session_cached(const fgl_session_t* s, fgl_prop_t prop,
fgl_value_t* out);
// Интроспекция шаблонов (превью HA, CLI).
int fgl_template_detect(const char* oem_model); // -1 — неизвестно
const char* fgl_prop_name(fgl_template_t t, fgl_prop_t prop); // NULL — нет
const char* fgl_prop_base_type(fgl_template_t t, fgl_prop_t prop);
int fgl_prop_read_only(fgl_template_t t, fgl_prop_t prop); // -1 — нет
// raw-диапазон свойства из таблицы шаблона: 1 — определён (min/max заполнены),
// 0 — диапазона нет, -1 — свойства нет в шаблоне.
int fgl_prop_raw_range(fgl_template_t t, fgl_prop_t prop, int64_t* min,
int64_t* max);
int64_t fgl_convert_to_display(fgl_template_t t, fgl_prop_t prop,
int64_t raw);
int64_t fgl_convert_from_input(fgl_template_t t, fgl_prop_t prop,
int64_t api_value);
#ifdef __cplusplus
} // extern "C"
#endif