"""cffi-декларации поверх include/fgl-aircon/c_api.h (ABI-режим, dlopen). Библиотека ищется в порядке: $FGL_AIRCON_LIB, рядом с пакетом (pyfglair/libfgl-aircon.so*), затем по системному имени (ldconfig). """ from __future__ import annotations import ctypes.util import os import re from pathlib import Path from cffi import FFI ffi = FFI() ffi.cdef( """ const char* fgl_version(void); 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); 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; 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; 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; 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); 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); int fgl_session_poll_events(fgl_session_t* s, fgl_event_t* out, int max_events); 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); 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); int fgl_template_detect(const char* oem_model); const char* fgl_prop_name(fgl_template_t t, fgl_prop_t prop); 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); 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); """ ) _cdll = None def find_library() -> str: env = os.environ.get("FGL_AIRCON_LIB") if env: if not Path(env).exists(): raise FileNotFoundError(f"FGL_AIRCON_LIB={env} не существует") return env pkg = Path(__file__).resolve().parent pattern = re.compile(r"^libfgl-aircon\.(so(\.[0-9.]+)?|dylib|\d+\.dylib)$") candidates = sorted( (p for p in pkg.iterdir() if p.is_file() and pattern.match(p.name)), key=lambda p: (p.name.count("."), len(p.name)), ) if candidates: return str(candidates[0]) system = ctypes.util.find_library("fgl-aircon") if system: return system raise OSError( "библиотека fgl-aircon не найдена. Соберите её из корня монорепо: " "python -m pyfglair._corebuild --inplace (или задайте FGL_AIRCON_LIB)" ) def get_library(): """dlopen ядра (однократно на процесс).""" global _cdll if _cdll is None: _cdll = ffi.dlopen(find_library()) return _cdll lib = get_library()