Skip to content

uk Architecture

wiki-sync edited this page Apr 15, 2026 · 1 revision

Архітектура системи SelenaCore

Зміст


Огляд

SelenaCore — це локальний хаб розумного дому, побудований на FastAPI, розроблений для роботи на маломіцному обладнанні, такому як Raspberry Pi. Вся логіка автоматизації, управління пристроями та обробка голосу виконуються на локальній машині. Хмарне з'єднання є необов'язковим і обмежується синхронізацією heartbeat та прийомом віддалених команд.

Основний технологічний стек:

Компонент Технологія
Веб-фреймворк FastAPI (порт 80)
База даних SQLite через SQLAlchemy 2.0 async
Асинхронний драйвер aiosqlite
Цикл подій Один цикл asyncio
Точка входу core/main.py (FastAPI lifespan)
Мова Python 3.11

Архітектура високого рівня

+------------------------------------------------------------------+
|                         SelenaCore Process                        |
|   port 80 (FastAPI)                                            |
|                                                                  |
|  +------------------+   +------------------+   +--------------+  |
|  | 21 SYSTEM        |   |    EventBus      |   |  Device      |  |
|  | modules          |<->| (asyncio.Queue)  |<->|  Registry    |  |
|  | (in-process)     |   |                  |   |  (SQLite)    |  |
|  +------------------+   +--------+---------+   +--------------+  |
|                                  |                               |
|                         +--------+---------+                     |
|                         |   Module Bus     |                     |
|                         |   (WebSocket)    |                     |
|                         +--------+---------+                     |
|                                  |                               |
+----------------------------------+-------------------------------+
                                   |
                    +--------------+--------------+
                    |              |              |
               +----+----+  +----+----+  +------+------+
               | Docker  |  | Docker  |  | Docker      |
               | Module  |  | Module  |  | Module      |
               | (user)  |  | (user)  |  | (user)      |
               +---------+  +---------+  +-------------+

  Окремий процес:
  +----------------------------+
  | Integrity Agent            |
  | SHA256 hash check / 30s   |
  | Safe mode enforcement      |
  +----------------------------+

Послідовність завантаження

Процедура запуску визначена в обробнику lifespan FastAPI у core/main.py. Кроки виконуються у строгому порядку:

1. _setup_logging()
   |  Читання logging.yaml або перехід на базову конфігурацію
   v
2. Create SQLAlchemy async engine + tables
   |  Ініціалізація бази даних SQLite
   v
3. Inject session factory into sandbox
   |  Системні модулі отримують доступ до бази даних
   v
4. EventBus.start()
   |  Споживач asyncio.Queue починає роботу
   v
5. Publish core.startup event
   |  Слухачі повідомлені
   v
6. CloudSync.start()
   |  Цикл heartbeat починається (необов'язково)
   v
7. Scan system_modules/ -> load in-process -> mount routers
   |  21 вбудовані модулі активовано
   v
8. Scan modules/ -> start user modules
   |  Docker-контейнери запущено, bus-з'єднання прийнято
   v
9. "SelenaCore ready on port 80"

Система модулів

SelenaCore підтримує два різних типи модулів, які використовують спільний EventBus, але принципово відрізняються способом виконання.

Системні модулі (in-process)

Властивість Значення
Кількість 21 вбудованих
Базовий клас SystemModule (core/module_loader/system_module.py)
Виконання In-process через Python importlib
Ізоляція Відсутня (спільний процес)
Витрати RAM ~0 МБ (без контейнера)
Доступ до EventBus Прямі async-колбеки (DirectSubscription)
Доступ до БД Пряма сесія SQLAlchemy
API Необов'язковий FastAPI роутер на /api/ui/modules/{name}/
Розташування Каталог system_modules/

Вбудовані системні модулі:

voice_core           llm_engine           ui_core
user_manager         automation_engine    scheduler
device_watchdog      protocol_bridge      notification_router
media_player         presence_detection   hw_monitor
backup_manager       remote_access        network_scanner
device_control       energy_monitor       update_manager
notify_push          secrets_vault        weather_service

Користувацькі модулі (Docker-контейнери)

Властивість Значення
Базовий клас SmartHomeModule (sdk/base_module.py)
Виконання Окремі Docker-контейнери
Комунікація WebSocket Module Bus
Bus endpoint ws://core/api/v1/bus?token=TOKEN
Окремі порти Немає — весь трафік через єдиний bus

Типи користувацьких модулів:

Тип Призначення
UI Панелі користувацького інтерфейсу
INTEGRATION Конектори до сторонніх сервісів
DRIVER Драйвери пристроїв та протоколів
AUTOMATION Логіка користувацької автоматизації
IMPORT_SOURCE Імпортери зовнішніх даних

Життєвий цикл модуля

  [Discovered]
       |
       v
  [Installed] --module.installed-->
       |
       v
  [Started]   --module.started--->  (підписка EventBus активна)
       |
       v
  [Running]   <-- нормальна робота -->
       |
       v
  [Stopped]   --module.stopped--->
       |
       v
  [Removed]   --module.removed--->

EventBus

Джерело: core/eventbus/bus.py

EventBus — це центральна нервова система SelenaCore. Це система публікації/підписки на основі asyncio.Queue з максимальним розміром черги 10 000 повідомлень та політикою видалення найстаріших при переповненні.

Канали доставки

  Publisher
      |
      v
  +---+-----------+
  |   EventBus    |
  | (Queue: 10K)  |
  +---+-------+---+
      |       |
      v       v
  Direct    Module Bus
  Subscr.   WebSocket
  (system)  (user modules)
  1. DirectSubscription — in-process async-колбеки, які використовуються системними модулями. Нульова вартість серіалізації, доставка за мікросекунди.
  2. Module Bus WebSocket — події серіалізуються в JSON і доставляються через WebSocket-з'єднання до користувацьких модулів, які працюють у Docker-контейнерах.

Простір імен подій

Усі типи подій визначені у core/eventbus/types.py:

Простір імен Події
core.* startup, shutdown, integrity_violation, safe_mode_entered, safe_mode_exited
device.* state_changed, registered, removed, offline, online, discovered
module.* installed, started, stopped, error, removed
sync.* command_received, command_ack, connection_lost, connection_restored
voice.* wake_word, recognized, intent, response, privacy_on, privacy_off

Правило захисту: Події у просторі імен core.* можуть публікуватися лише основним процесом. Модулі не можуть створювати події core.


Module Bus

Джерело: core/module_bus.py

Module Bus — це комунікаційний рівень, натхненний CAN-bus, який мультиплексує весь трафік користувацьких модулів через єдиний WebSocket endpoint.

Принципи проєктування

  • Core є головним вузлом. Модулі підключаються ДО core, ніколи навпаки.
  • Єдиний endpoint: /api/v1/bus — без окремих портів для модулів.
  • Подвійні черги повідомлень на кожне з'єднання для розділення критичного та некритичного трафіку.

Типи повідомлень

Повідомлення Напрямок Призначення
announce module -> core Модуль реєструється при підключенні
re_announce module -> core Модуль перереєструється після перепідключення
announce_ack core -> module Реєстрацію підтверджено
intent core -> module Інтент маршрутизовано до обробника
intent_response module -> core Обробник повертає результат
event двонаправлений Пересилання подій EventBus
ping / pong двонаправлений Keepalive
api_request module -> core Модуль викликає API core
api_response core -> module Core повертає результат API
shutdown core -> module Сигнал коректного завершення

Архітектура подвійних черг

Кожне WebSocket-з'єднання підтримує дві незалежні черги:

  Module Connection
  +---------------------------------------+
  |                                       |
  |  Critical Queue (backpressure)        |
  |  - Max size: 100                      |
  |  - Used for: intent, api_request,     |
  |    api_response, intent_response      |
  |  - Blocks sender when full            |
  |                                       |
  |  Event Queue (drop-oldest)            |
  |  - Max size: 1000                     |
  |  - Used for: event messages           |
  |  - Drops oldest when full             |
  |                                       |
  +---------------------------------------+

Така архітектура гарантує, що потік некритичних подій ніколи не блокує обробку інтентів або API-викликів.

Circuit Breaker

Якщо модуль не відповідає протягом 30 секунд, bus активує circuit breaker для цього модуля. Модуль тимчасово виключається з маршрутизації інтентів до відновлення.

Дозволи ACL

Кожен тип модуля має попередньо визначений набір дозволених типів повідомлень та підписок на події. Bus перевіряє ці дозволи для кожного повідомлення.


Синхронізація UI (WebSocket)

Джерело: core/api/sync_manager.py, core/api/routes/ui.py

Синхронізація стану UI (тема, мова, розташування віджетів) у реальному часі між усіма підключеними клієнтами через WebSocket /api/ui/sync.

Властивість Значення
Ендпоінт ws://host/api/ui/sync?v=<version>
Протокол JSON-повідомлення з монотонним версіонуванням
Стан Налаштування (тема, мова) + розташування віджетів
При підключенні Повний знімок (hello) або дельта-повтор (replay)
Health check Серверний ping кожні 5с, клієнт pong протягом 15с
Бекенд SyncManager синглтон з deque(256) логом подій
Фронтенд Zustand store connectSyncStream() з exponential backoff
Безпека кіоску useConnectionHealth хук — примусове перезавантаження після 60с мовчання

SPA (React) та всі API-ендпоінти обслуговуються одним процесом на порту 80. HTTPS на порту 443 обслуговується легким TLS-проксі (~5 МБ RAM).

Дивіться Архітектура синхронізації UI для повного опису протоколу та нотаток міграції.


Система інтентів

Джерело: system_modules/llm_engine/intent_router.py

Маршрутизатор інтентів використовує 5-рівневий каскад. Кожен рівень перевіряється по порядку; перший збіг виграє. Уся pipeline працює англійською внутрішньо — починаючи з v0.4 переклад виконується Argos Translate на краях пайплайну (після Vosk STT, перед Piper TTS), а не LLM. Українських / російських / німецьких FastMatcher-патернів немає за дизайном.

  Голосова команда (будь-якою мовою)
       │
       ▼
  ┌──────────────────────────────────────────────────────────────┐
  │  Tier 1   FastMatcher (regex з БД, тільки English)  ~0 мс   │
  │  Tier 2   Module Bus (модулі користувача, WS)       ~мс     │
  │  Cache    IntentCache (SQLite, попередні LLM hits)  ~10 мс  │
  │  Tier 3   Local LLM (Ollama, один виклик)           300-800 │
  │  Tier 4   Cloud LLM (OpenAI-сумісний, опціонально)  1-3 сек │
  │  Fallback "не зрозумів" (i18n)                              │
  └──────────────────────────────────────────────────────────────┘
       │
       ▼  EventBus: voice.intent { intent, params, source }
  Модуль-власник інтенту виконує
       │
       ▼
  Відповідь (LLM rephrase для варіативності) → TTS

Деталі рівнів:

Рівень Джерело Затримка Механізм Примітки
1 FastMatcher (IntentCompiler) ~0 мс Regex з БД з пріоритетом + specificity-сортуванням + verb-bucket pre-filter Тільки англійська
2 Module Bus ~мс WebSocket запит до user-installed модулів Per-module circuit breaker
Cache IntentCache (SQLite) ~10 мс Lookup за (text, lang) ключем для попередніх LLM hits Усі мови
3 Local LLM (Ollama) 300-800 мс Один виклик класифікації з динамічним registry-aware промптом Потребує ~3-5 ГБ RAM
4 Cloud LLM 1-3 сек OpenAI / Anthropic / Groq класифікація Опційно
Fallback ~0 мс i18n повідомлення "не зрозумів"

Жорсткі інтенти приходять з модулів, не зі seed-скриптів. Кожен системний модуль декларує свої OWNED_INTENTS і _OWNED_INTENT_META та inserts/claims рядки в intent_definitions на start() через _claim_intent_ownership(). Центрального seed-файлу немає.

Composite-патерни пристроїв масштабуються O(1) у DB рядках. PatternGenerator.rebuild_composite_device_patterns() створює максимум 5 рядків на весь реєстр пристроїв — по одному на дієслово (device.on, device.off, device.set_temperature, device.lock, device.unlock) — кожен з (?P<name>...) alternation усіх відомих імен пристроїв. Захоплене ім'я резолвиться у device_id через in-memory індекс за O(1).

Verb-bucket pre-filter скорочує типову довжину FastMatcher scan з O(всі-патерни) до ~3-15 кандидатів. _VERB_BUCKETS мапа маршрутизує перше слово вводу (turn, set, play, what, lock, …) до малого набору кандидатних інтентів.

Динамічний LLM-промпт з registry context. Tier 3 промпт перебудовується при кожному device CRUD і містить: зареєстровані інтенти (з описами), підключені модулі з їх інтентами, пристрої згруповані за meta.location_en, і список відомих індор-кімнат. Дві константи cap'лять розмір: _DEVICES_PER_ROOM_LIMIT=10 і _ROOMS_LIMIT=30. Це те, що дозволяє LLM розрізнити "what is the temperature in the living room" (→ device.query_temperature) і "what is the temperature outside" (→ weather.temperature) без жодного хардкодного мапу.

IntentCache промоція. Гарячі фрази, які hit'нули кеш >=5 разів, промотуються у FastMatcher-патерни раз на годину з core/main.py lifespan. Promoted рядки використовують source='auto_learned' відокремлений namespace від auto_entity. Тільки англійською за дизайном.

LLM Rephrase відповідей: Після виконання голосової команди модулем voice-core відправляє структурований action context до rephrase LLM (temperature=0.9). Це дає природні TTS-відповіді замість шаблонних рядків. Сесія діалогу (останні 20 повідомлень, таймаут 5 хв) забезпечує контекст для зв'язного спілкування.

Повні деталі імплементації — pattern specificity scoring, composite resolver, дисамбігуація ambiguous імен, структура промпту, межі масштабування — у intent-routing.md.


Реєстр пристроїв

Джерело: core/registry/

Реєстр пристроїв — це постійне сховище для всіх відомих пристроїв, їхнього поточного стану та історичних даних.

Схема бази даних (SQLAlchemy ORM)

Таблиця Device:

Стовпець Тип Опис
device_id UUID Первинний ключ, автогенерований
name String Зручна для людини назва пристрою
type String Категорія пристрою (light, sensor тощо)
protocol String Протокол зв'язку (zigbee, mqtt...)
state JSON Поточний стан пристрою
capabilities JSON Підтримувані функції та діапазони значень
last_seen DateTime Часова мітка останнього зв'язку
module_id String Ідентифікатор модуля-власника
meta JSON Довільні метадані

Таблиця StateHistory:

  • Зберігає останні 1 000 змін стану для кожного пристрою.
  • Кожен запис містить попередній стан, новий стан та часову мітку.
  • Старіші записи видаляються автоматично.

Таблиця AuditLog:

  • Зберігає до 10 000 записів з автоматичною ротацією.
  • Логує адміністративні дії: реєстрацію пристроїв, видалення, зміни конфігурації.

Рівень API

Конвеєр Middleware

Запити проходять через middleware у такому порядку:

  Incoming request
       |
       v
  RequestIdMiddleware       -- Присвоює унікальний заголовок X-Request-ID
       |
       v
  RateLimitMiddleware       -- 120 запитів за 60 секунд
       |
       v
  CORSMiddleware            -- Політика крос-доменних запитів
       |
       v
  Route handler

Аутентифікація

  • Аутентифікація за допомогою Bearer token для доступу модулів та зовнішнього API.
  • Токени зберігаються у /secure/module_tokens/.
  • Маршрути UI (/api/ui/*) не потребують аутентифікації, але доступні лише з localhost.

Групи маршрутів

Core API (/api/v1/*) — з аутентифікацією:

Маршрут Призначення
/system Інформація про систему, стан здоров'я
/devices CRUD пристроїв та запити стану
/events Інспекція та публікація подій EventBus
/integrity Статус та звіти перевірки цілісності
/modules Управління життєвим циклом модулів
/secrets Доступ до сховища секретів
/intents Маршрутизація та тестування інтентів
/bus WebSocket endpoint Module Bus

UI API (/api/ui/*) — лише localhost, без аутентифікації:

Маршрут Призначення
/ui Обслуговування панелі UI
/setup Майстер початкового налаштування
/voice_engines Конфігурація голосових рушіїв

Документація Swagger

Доступна за адресою /docs лише коли DEBUG=true у змінних середовища.


Хмарна синхронізація

Джерело: core/cloud_sync/sync.py

Хмарне з'єднання є необов'язковим і спроєктоване як мінімальне. Core ніколи не залежить від доступності хмари для локальної роботи.

Параметр Значення
Віддалений сервер selenehome.tech
Інтервал heartbeat 60 секунд
Підпис запитів HMAC-SHA256
Тайм-аут опитування команд 55 секунд (long-poll)
Відступ (початковий) 5 секунд
Відступ (максимальний) 300 секунд
Стратегія відступу Експоненційна
  SelenaCore                          selenehome.tech
     |                                      |
     |--- heartbeat (HMAC-SHA256) --------->|
     |<-- 200 OK ---------------------------|
     |                                      |
     |--- long-poll /commands ------------->|
     |         (55s timeout)                |
     |<-- command payload ------------------|
     |                                      |
     |--- command ack --------------------->|
     |                                      |

При збої мережі клієнт синхронізації відступає експоненційно від 5 до 300 секунд перед повторною спробою.


Конфігурація

SelenaCore використовує модель конфігурації з двох джерел.

Змінні середовища (.env)

Керуються через Pydantic BaseSettings у core/config.py за допомогою класу CoreSettings. Усі поля типізовані та валідуються при запуску.

Основні налаштування:

Змінна Значення за замовчуванням Опис
CORE_PORT 80 Порт прослуховування FastAPI
CORE_DATA_DIR /var/lib/selena Каталог постійних даних
CORE_SECURE_DIR /secure Сховище токенів та секретів
DEBUG false Увімкнення режиму налагодження та /docs

YAML-конфігурація (core.yaml)

Використовується для структурованої конфігурації, яка погано вписується у плоскі змінні середовища (налаштування модулів, пресети логування, правила автоматизації).

Пріоритет: Змінні середовища перевизначають значення YAML, де обидва джерела визначають однакове налаштування.


Інтернаціоналізація

Фронтенд: src/i18n/locales/{en,uk}.ts через i18next + react-i18next.

Голосові відповіді: Генеруються LLM в реальному часі через _generate_via_llm() у VoiceCoreModule. Voice handler'и повертають структуровані dict'и з контекстом дії; LLM генерує природну відповідь мовою TTS. Без попередньо написаних перекладів чи кешування — кожна відповідь генерується заново.

HTML-віджети: Вбудовані словники var L = {en:{…}, uk:{…}} з атрибутами data-i18n.


Агент цілісності

Джерело: agent/

Агент цілісності працює як окремий процес поряд із core. Це сторожовий модуль, який забезпечує, що кодова база core не була змінена.

Цикл роботи

  every 30 seconds:
       |
       v
  Compute SHA256 hashes of core files
       |
       v
  Compare against known-good manifest
       |
       +-- match -----> OK, sleep 30s
       |
       +-- mismatch --> VIOLATION DETECTED
                             |
                             v
                        Stop all modules
                             |
                             v
                        Notify (core.integrity_violation event)
                             |
                             v
                        Attempt rollback
                             |
                             v
                        Enter SAFE MODE
                             |
                             v
                        Publish core.safe_mode_entered

У безпечному режимі залишаються активними лише основні функції core. Усі користувацькі модулі зупиняються і не можуть бути перезапущені, доки проблему цілісності не буде вирішено.


Розгортання

Архітектура Docker Compose

  docker-compose.yml
  +--------------------------------------------------+
  |                                                  |
  |  +------------------+    +-------------------+   |
  |  | core             |    | agent             |   |
  |  | Dockerfile.core  |    | Integrity Agent   |   |
  |  | Host networking  |    | Separate process  |   |
  |  | Privileged mode  |    |                   |   |
  |  +------------------+    +-------------------+   |
  |         |                        |               |
  |         v                        v               |
  |  +-------------+    +------------------+         |
  |  | selena_data |    | selena_secure    |         |
  |  | (volume)    |    | (volume)         |         |
  |  +-------------+    +------------------+         |
  |                                                  |
  +--------------------------------------------------+

Контейнер Core (Dockerfile.core)

Властивість Значення
Базовий образ python:3.11-slim
Режим мережі host
Привілеї privileged (доступ до обладнання)
Системні пакети ffmpeg, portaudio, VLC, ALSA libs, PulseAudio

Мережа host та привілейований режим необхідні для:

  • Прямого доступу до аудіообладнання (мікрофон, динаміки) для обробки голосу.
  • Доступу до USB-пристроїв та GPIO-пінів для мостів протоколів (Zigbee, Z-Wave dongles).
  • Multicast/broadcast для протоколів виявлення пристроїв.

Томи

Том Призначення
selena_data База даних, дані модулів, логи, резервні копії
selena_secure Токени, секрети, сертифікати

Послідовність завершення роботи

Коректне завершення роботи дзеркально відображає послідовність завантаження у зворотному порядку, гарантуючи відсутність втрати даних:

1. CloudSync.stop()
   |  Зупинка heartbeat та опитування команд
   v
2. Publish core.shutdown event
   |  Усі слухачі повідомлені
   v
3. Module Bus shutdown_all(drain_ms=5000)
   |  Надсилання shutdown усім користувацьким модулям
   |  Очікування до 5 секунд для дренажу черг
   v
4. Shutdown in-process system modules
   |  Виклик stop() кожного системного модуля
   v
5. EventBus.stop()
   |  Споживач черги зупинено
   v
6. Database engine dispose
   |  Усі з'єднання закриті, WAL checkpoint
   v
   Process exit

5-секундне вікно дренажу для Module Bus гарантує, що користувацькі модулі мають час зберегти свій стан та підтвердити завершення роботи до того, як їхні WebSocket-з'єднання будуть розірвані.


Додаткові матеріали

Тема Документ
Аутентифікація користувачів та QR-процес user-manager-auth.md
Протокол модулів (токени, HMAC, webhooks) module-core-protocol.md
Дротовий протокол Module Bus module-bus-protocol.md
Розробка модулів (SDK, маніфест) module-development.md
Розробка віджетів (widget.html, i18n) widget-development.md
Довідник конфігурації configuration.md
Розгортання та systemd deployment.md

Clone this wiki locally