Перейти к содержанию

Веб-панель управления

HTTP-сервис отдаёт панель управления в браузере по адресу /; документация OpenAPI переехала на /docs. Запустите сервис, откройте страницу и управляйте игрушками без единой строчки кода — подключение, переподключение и опрос состояния сервис берёт на себя.

pip install 'lovensepy[service,ble]'
lovensepy-service        # выберет свободный порт и откроет панель в браузере

lovensepy-service по умолчанию запускается в режиме hybrid, поэтому BLE-управление доступно без переменных окружения; LOVENSE_PORT, LOVENSE_HOST и LOVENSE_OPEN_BROWSER=0 переопределяют его выбор. Чтобы запустить под своим сервером:

LOVENSE_SERVICE_MODE=hybrid uvicorn lovensepy.services.http_api.app:app --host 0.0.0.0 --port 8000
# затем откройте http://127.0.0.1:8000/

Готовые сборки (.dmg для macOS / .exe для Windows) — тот же лаунчер, они открывают панель сами.

Что умеет

Раздел Поведение
Devices Карточка на игрушку: слайдер на каждый мотор (POST /command/function), четыре пресета Lovense, редактор паттернов, пульсация на стороне устройства, стоп по игрушке.
Running Все строки планировщика из GET /tasks с живым обратным отсчётом и кнопкой стоп.
Connect Скан Bluetooth и Connect everything одним нажатием (POST /ble/connect/auto), форма адреса Game Mode (POST /config/lan-ip), QR-код облачной привязки Lovense (POST /config/socket, GET /socket/qr).
Settings Включение транспортов, настройка Bluetooth (автопереподключение, фоновый скан, диалект пресетов, фильтр скана), тема, блокировка гашения экрана.
Шапка Состояние связи, Stop all (POST /command/stop/all) и QR-код с LAN-адресами этой машины (GET /system/network) для телефона.

В браузере хранятся только настройки интерфейса (тема, последняя длительность, wake lock). Учётные данные, введённые в Connect, живут только в памяти процесса сервиса.

Доступ с телефона

Откройте панель на телефоне в той же сети Wi-Fi: нажмите иконку телефона в шапке на компьютере и отсканируйте QR-код либо введите LAN-адрес вручную. Приложение устанавливать не нужно, привязки нет — это тот же сервис и то же живое состояние, поэтому компьютер и телефон могут управлять одной игрушкой одновременно.

Страница отдаётся по обычному HTTP на LAN-адрес, поэтому часть браузерных возможностей вне защищённого контекста недоступна; панель это учитывает (копирование адреса уходит в устаревший путь, переключатель wake lock скрывается там, где API нет).

Как поддерживается синхронизация

  • GET /state отдаёт один снимок: игрушки, объединённые по транспортам, активные сессии, обнаружение Bluetooth и статус супервизора, состояние привязки Socket API, возможности и конфигурацию. Одного запроса хватает, чтобы отрисовать панель целиком.
  • WS /ws присылает этот снимок при каждом изменении, между ними — heartbeat. Если сокет открыть нельзя (строгий прокси, старый Safari по http), браузер переходит на опрос GET /state и продолжает пробовать сокет в фоне.
  • Уснувший телефон подвешивает сокет, не закрывая его, поэтому панель следит за тишиной, принудительно переподключается при возврате вкладки и перечитывает состояние на online / pageshow.
  • Движения слайдера объединяются: в полёте не больше одного запроса на мотор, из ожидания выживает только самое новое значение, а значение на отпускании отправляется последним. Иначе каждое перетаскивание ставило бы в очередь десятки Bluetooth-записей и игрушка отставала бы от пальца.
  • Зарегистрированные Bluetooth-игрушки сервис держит подключёнными сам (экспоненциальная задержка на игрушку, пауза только если вы отключили её вручную), поэтому уехавшая из радиуса игрушка возвращается без вашего участия. См. GET /state → ble.supervisor.

Переменные окружения

Всё, чем управляет панель, можно задать и заранее. Обязательного тут нет — панель умеет настраивать транспорты на ходу.

Переменная По умолчанию Значение
LOVENSE_WEBUI 1 0 — только API: / отдаёт 404, /docs работает.
LOVENSE_CORS_ORIGINS (пусто) Дополнительные разрешённые origins через запятую. * допустим, но открывает API любой открытой странице.
LOVENSE_CORS_ALLOW_LOCALHOST 1 Разрешать origins localhost / 127.0.0.1 / [::1] (dev-сервер Vite).
LOVENSE_STATE_CACHE_TTL 2 Секунды кэша списка игрушек для GET /state и /ws. GET /toys не кэшируется.
LOVENSE_EVENTS_INTERVAL 1 Секунды между отправками в /ws; одинаковые снимки пропускаются.
LOVENSE_BLE_AUTO_RECONNECT 1 Держать зарегистрированные Bluetooth-игрушки подключёнными в фоне.
LOVENSE_BLE_AUTO_RECONNECT_INTERVAL 5 Секунды между раундами супервизора.
LOVENSE_BLE_BATTERY_REFRESH 120 Секунды между чтениями заряда на игрушку.
LOVENSE_TUNNEL 0 1 — вместе с сервисом поднимает Cloudflare quick tunnel (cloudflared tunnel --url …). Публичный https://*.trycloudflare.com появляется в QR-диалоге телефона и в Settings → Phone from anywhere.
LOVENSE_CLOUDFLARED_BIN (PATH) Опциональный абсолютный путь к бинарнику cloudflared.
LOVENSE_TUNNEL_HOST 127.0.0.1 Loopback-хост, к которому стучится cloudflared (оставьте по умолчанию).
LOVENSE_GATE 1 Вне локальной сети посетитель вводит 6-значный код из консоли сервиса. 0 — только если вы сознательно хотите открытую публичную панель.

Сначала установите cloudflared (brew install cloudflared на macOS). В той же Wi-Fi доступ свободный; туннель / публичный hostname показывает страницу подтверждения и печатает код в консоли, где вы запустили сервис.

Переменные транспортов (LOVENSE_SERVICE_MODE, LOVENSE_LAN_IP, учётные данные Socket, параметры скана BLE) — в руководстве по сервису FastAPI.

Сборка из исходников

В wheel и готовых сборках панель уже собрана. В чистом git-клоне её нет: сервис отдаёт короткую страницу-заглушку (API продолжает работать). Соберите один раз с Node 22+:

cd frontend
npm ci
npm run build     # → lovensepy/services/http_api/webui_dist/

Для разработки интерфейса запустите сервис на :8000 и Vite рядом — API-пути и /ws проксируются, поэтому dev-сервер ведёт себя как собранная версия:

cd frontend
npm run dev       # http://127.0.0.1:5173
# другой адрес сервиса: LOVENSE_SERVICE_URL=http://192.168.1.10:8000

Стек: React 19 + TypeScript + Vite, Tailwind CSS v4 и компоненты shadcn/ui. npm run build сначала проверяет типы, поэтому ошибка типизации ломает сборку.