2026-08-21 19:45:43 +03:00

ViewMate

Очки RayNeo X2 + компаньон на телефон: кино и чат с ассистентом.

Статус реализации (19.08.2026)

Возможность Статус
Бинокулярный видеорендер на очках (ExoPlayer → EGL-блит в оба глаза), fullscreen ✅ этап 1
Пульт: пауза/плей/±30с/закрыть (с телефона и из консоли) ✅
Связка очки↔телефон по WiFi (WS), два эмулятора + adb forward ✅
Каталог Jellyfin на телефоне (Фильмы/Сериалы/Папки, поиск, постеры, бейджи зеркала) ✅
Детали: выбор озвучки, статус конвертации, заказ зеркала ✅
Реальное скачивание с S3 (SigV4, прогресс, докачка с места, персистентный статус) ✅
Экран «Очки»: связь, батарея/память, пульт ✅
Синхронизированный звук на телефоне (очки без динамиков) ✅ 19.08 (diff < 1000 мс, авто-ресинк)
Чат с ассистентом (режим CHAT, сессии Hermes, голосовой ассистент) ❌ заглушка
Жесты RayNeo (пауза/перемотка жестом) ❌ в планах
NSD-дискавери телефона (реальные очки находят телефон по сети) ✅ 19.08: mDNS + скан /24 (на RayNeo multicast режется — работает скан; эмулятор — fallback 10.0.2.2)

Подробный журнал решений и инцидентов: /root/notes/2026-08-19.md (внутреннее).

Структура

app-glasses/   RayNeo X2 — AR-приложение: видео (ExoPlayer), жесты, связь по WiFi
app-phone/     компаньон на телефон — каталог, дубль-управление, чат
app-host/      отладочный хост — «бэк» без UI, к которому цепляются очки
lib-core/      общий слой (KMP): режимы, состояния, команды, DTO
sketches/      эскизы UI: раскладка экранов + сценарии использования

Эскизы интерфейса

  • sketches/phone-ui/index.html — эскизы 4 экранов телефона (раскладка компонентов, открыть в браузере)
  • sketches/phone-ui/preview.png — скриншот эскизов
  • sketches/phone-ui/FLOW.md — сценарии использования: как посмотреть фильм, проверить очки, поговорить с ассистентом, что видно при не-сконвертированном/частично скачанном контенте

Режимы

Два режима на очках

Режим Состояния Что происходит
MOVIE (кино) PLAYING, PAUSED видео на очках (ExoPlayer), звук выбранной дорожки на телефоне, синхронизация по позиции
CHAT (общение) SESSION_SELECT, TALKING выбор сессии Hermes, затем разговор в рамках сессии

Переключение режимов — с телефона (и жестом на очках). Оба дёргают один локальный агент (lib-core).

Каталог — НЕ режим очков

Каталог контента (Jellyfin) живёт только на телефоне (и в будущем — у голосового ассистента, который тоже будет по нему ходить). Выбрал в каталоге → «включить» → очки переходят в режим MOVIE с готовым контентом.

Очки — «экран кинотеатра»: они ждут, когда им скажут «включи», и не листают каталог сами.

Локальный голосовой ассистент (в будущем)

Вызывается в любом режиме (двойной клик в чате), поверх всего. Умеет: ходить по каталогу, включать/ставить кино, переключать режимы, переключать сессии, отвечать на вопросы (локально, без сервера).

Жесты (распознаёт RayNeo SDK, TempleAction)

Вне распознавания (обычный режим)

Жест (SDK) Режим КИНО Режим ЧАТ
Click пауза ↔ плей старт / стоп диктовки
DoubleClick игнор отмена диктовки
TripleClick безусловный вызов ассистента (пауза видео + затемнение + слушаем) безусловный вызов ассистента (слушаем)
SlideBackward / SlideForward перемотка в SESSION_SELECT — листание сессий; в TALKING — скролл текста ответа
LongClick (резерв) показать / спрятать текст последнего ответа

Во время распознавания речи (LISTENING)

Жест Действие
Click завершить распознавание: вывести весь текст (все фразы) в консоль; затемнение уходит, видео остаётся на паузе
DoubleClick отмена распознавания (текст отбрасывается)
TripleClick игнор
VAD молчит 30 с автоматическая отмена распознавания

TripleClick — безусловный вызов локального ассистента из любого места (поверх всего). Распознавание идёт по фразам: VAD отсёк фразу — распознал — отобразил на очках — слушаем дальше; ввод считается открытым до Click.

Жесты распознаёт сам SDK (TempleAction.Click/DoubleClick/SlideBackward/ SlideForward/LongClick) — своя детекция не нужна (проверено в pair-view, MainActivity.kt). TripleClick: если SDK его не отдаёт — детект по таймстампам кликов в onTouchEvent.

Ассистент (потоковый): TripleClick → говорите → каждая фраза уходит в LLM (llm.binom.pw), ответ текстом на очки; история диалога — в SQLite на телефоне. Модель/ключ: local.properties llm.model/llm.apiKey.

Состояния ассистента (единые для обоих ассистентов)

IDLE ──► LISTENING ──► THINKING ──► SPEAKING ──► IDLE
        (слушает)     (думает)     (говорит)
Состояние Индикатор
IDLE ничего
LISTENING красная точка ● мигает + время записи (0:07, 0:23…)
THINKING спиннер / точки «думает»
SPEAKING «говорит» (другим цветом / без мигания)

Один и тот же индикатор для Порфирия и локального ассистента — очкам неважно, кто за ним стоит.

Панель индикаторов

Всегда видна, по центру снизу. Содержимое меняется от режима:

Режим/состояние Содержимое панели
КИНО просмотрено / осталось (12:34 / 56:00) + 🔋 батарея + ⏰ часы
ЧАТ, диктовка ● 0:07 (красная точка + время записи) + 🔋 + ⏰
ЧАТ, думает/говорит индикатор состояния (⏳/🔊) + 🔋 + ⏰
Потеря связи с телефоном вместо времени — «связь потеряна» (⚠️) + 🔋
  • Часы — время очков (часы очков, просто время)
  • Батарея — очков
  • Просмотрено/осталось — из PlaybackState (positionMs/durationMs)
  • Если связи с телефоном нет — панель показывает ⚠️ вместо времени (см. Автоматика)

Текст ответа на экране

Длинные ответы можно показать текстом (по LongClick) и скроллить свайпами. Полезно, когда ответ многословный — перечитать спокойно.

Автоматика

Событие Действие
Очки выключились / экран погас пауза (видео и аудио)
Потеря связи очки ↔ телефон (WiFi) пауза (и на очках, и на телефоне) + на панели ⚠️ «связь потеряна» вместо времени
Связь восстановилась продолжить с того же места, панель — как обычно

Связь и поток

очки (RayNeo X2)                телефон (компаньон)              сервер
┌─────────────────┐            ┌──────────────────┐
│ режимы:         │            │ каталог Jellyfin  │            Jellyfin
│ MOVIE/CHAT      │◄──WiFi────►│ статусы зеркал    │──media-mirror API──► координатор
│ жест = switch   │            │ заказ конвертации │                  → воркеры → S3
│ видео ExoPlayer │            │ переключение      │
│                 │            │ режимов очков     │
│                 │            │ чат с Hermes      │──assistent/chat──► Hermes API
└─────────────────┘            └──────────────────┘                  → TTS → в ухо
  • Один локальный агент (на телефоне) — очки и телефон два рендера одного состояния
  • WiFi-связь очки ↔ телефон (вместо Bluetooth в старом pair-view)
  • Кино: видео 480p H.264 из S3 (media-mirror) на очках, звук выбранной дорожки на телефоне, синхронизация по позиции (очки — мастер, телефон подстраивает аудио)
  • Чат: с Hermes через сервер; медиа-интенты («включи сериал X») — напрямую в media-mirror API, без LLM

Медиа-конвейер (внешний, уже живой)

  • Координатор: https://mirror.binom.pw (X-API-Key) — заказ конвертации, статусы, файлы
  • Формат зеркала: video.mkv (H.264/NVENC, 480p, без звука) + audio-N.ogg (все дорожки, мета title/language)
  • S3: https://s3.binom.pw, bucket media, ключи mirror/{itemId}/...; клиентский ключ (read-only) — в конфиге

Полное описание API: media-mirror-api/README.md (https://git.binom.pw/subochev/media-mirror-api).

S
Description
Очки RayNeo X2 + компаньон на телефон: кино и чат с ассистентом
Readme 4.7 GiB
1 Latest
2026-08-21 17:05:33 +00:00
Languages
Kotlin 98.4%
HTML 1.6%