Веб-панель управления¶
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+:
Для разработки интерфейса запустите сервис на :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 сначала проверяет типы, поэтому ошибка типизации ломает сборку.