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
This commit is contained in:
2026-09-29 12:03:44 +03:00
parent 397be6236d
commit 26d1d6d64f
21 changed files with 1400 additions and 20 deletions
+17 -4
View File
@@ -65,12 +65,13 @@ class _LogPump(threading.Thread):
def __init__(self) -> None:
super().__init__(name="pyfglair-log", daemon=True)
self._stop = threading.Event()
def run(self) -> None:
out = ffi.new("fgl_log_event_t[]", 8)
dropped = 0
warned_at = 0.0
while True:
while not self._stop.is_set():
total = int(lib.fgl_log_events_dropped())
if total > dropped:
if time.monotonic() - warned_at > 30.0:
@@ -80,7 +81,7 @@ class _LogPump(threading.Thread):
)
warned_at = time.monotonic()
dropped = total
count = lib.fgl_log_wait_events(out, 8, 500)
count = lib.fgl_log_wait_events(out, 8, 250)
handler = _GLOBAL_LOG
if handler is None:
continue
@@ -93,13 +94,25 @@ class _LogPump(threading.Thread):
except Exception:
_LOGGER.exception("log handler failed")
def stop(self) -> None:
self._stop.set()
self.join(timeout=2.0)
def set_log_handler(handler: Optional[LogHandler]) -> None:
"""Глобальный приёмник логов ядра (0=debug..3=error)."""
"""Глобальный приёмник логов ядра (0=debug..3=error).
``None`` останавливает pump-поток (если он запущен).
"""
global _GLOBAL_LOG, _LOG_PUMP
with _LOG_PUMP_LOCK:
_GLOBAL_LOG = handler
if handler is not None and _LOG_PUMP is None:
if handler is None:
pump, _LOG_PUMP = _LOG_PUMP, None
if pump is not None:
pump.stop()
return
if _LOG_PUMP is None:
_LOG_PUMP = _LogPump()
_LOG_PUMP.start()