12 KiB
Magic4PC — WebOS Client
Управление LG MagicRemote как мышью/клавиатурой на HTPC через Bazzite.
Архитектура
- TV (WebOS) — app
me.wouterdek.magic4pc+ JS-сервисme.wouterdek.magic4pc.service- App: React/Enact UI, слушает кнопки пульта, отправляет в сервис через luna
- Сервис: Node.js, бродкастит UDP, принимает подключение от Go-клиента на HTPC
- HTPC (Bazzite) — Go-клиент
~/magic4pc/, сервисmagic4pc.service- Получает UDP-пакеты от TV, инжектирует ввод через
/dev/uinput inputBackKey()→ Esc в gamescope, x1 в KDE- Репо: https://github.com/mallexxx/magic4pc_altclient (ветка
main)
- Получает UDP-пакеты от TV, инжектирует ввод через
Исходники
| Компонент | Путь |
|---|---|
| WebOS UI | ~/Developer/magic4pc/webos/src/views/MainPanel.js |
| WebOS сервис | ~/Developer/magic4pc/webos/service/m4p_service.js |
| Go клиент | ~/Developer/magic4pc_altclient/ |
| HTPC бинарник | ~/magic4pc/magic4pc |
| HTPC сервис | ~/.config/systemd/user/magic4pc.service (HTPC) |
| run.sh | ~/magic4pc/run.sh (HTPC) |
Кнопки пульта (keyCode)
| Код | Кнопка | Действие |
|---|---|---|
| 406 | 🔵 Синяя | Открыть панель настроек |
| 461 | Back | Esc (gamescope) / x1 (KDE) |
| 458 | Guide | системный (не перехватывается) |
| 403 | 🔴 Красная | передаётся на HTPC |
| 415 | Play | передаётся на HTPC |
| 19 | Pause | передаётся на HTPC |
Панель настроек (синяя кнопка)
- Статус — Connected to IP:port / Waiting / Service disabled
- v1.1.0 (YYYY-MM-DD HH:MM) — версия + дата сборки справа от статуса
- Input source — выбор HDMI входа
- Enable / Disable — старт/стоп сервиса
- Default app on exit — приложение которое запускается при выходе из magic4pc (None / Last used / конкретное)
- Show Log / Hide Log — лог событий и ошибок поверх видео
Версия сборки
Версия отображается как 1.1.0 (YYYY-MM-DD HH:MM) — дата инжектируется webpack автоматически при каждой сборке:
// config/webpack.config.js
'process.env.BUILD_DATE': JSON.stringify(new Date().toISOString().slice(0, 16).replace('T', ' '))
Сборка WebOS app
cd ~/Developer/magic4pc/webos
NODE_OPTIONS=--openssl-legacy-provider npm run build
Требует --openssl-legacy-provider — старый webpack несовместим с Node 25.
Сборка IPK
cd ~/Developer/magic4pc/webos
NODE_OPTIONS=--openssl-legacy-provider npm run build
/Users/admin/webOS_TV_SDK/CLI/bin/ares-package dist/ service/ --outdir .
# Создаёт me.wouterdek.magic4pc_1.1.0_all.ipk
⚠️
ares-package dist/ service/— оба аргумента обязательны, иначе сервис не попадёт в IPK.npm run package=ares-package -n(без подписи) — не использовать, TV отклоняет IPK.
Деплой на TV (192.168.1.75)
TV рутован, SSH без пароля: ssh root@192.168.1.75
⚠️
luna-sendчерез SSH не выводит ответ в stdout — нужен PTY черезscript:ssh root@192.168.1.75 "script -q -c \"luna-send -n 1 -f luna://...\" /tmp/o.txt; cat /tmp/o.txt"
# 1. Скопировать IPK на TV
scp me.wouterdek.magic4pc_1.1.0_all.ipk root@192.168.1.75:/tmp/magic4pc.ipk
# 2. Закрыть приложение
ssh root@192.168.1.75 "script -q -c \"luna-send -n 1 -f luna://com.webos.service.applicationmanager/closeByAppId '{\\\"id\\\":\\\"me.wouterdek.magic4pc\\\"}'\" /tmp/o.txt; cat /tmp/o.txt"
# 3. Удалить старую версию
ssh root@192.168.1.75 "script -q -c \"luna-send -n 1 -f luna://com.webos.appInstallService/dev/remove '{\\\"id\\\":\\\"me.wouterdek.magic4pc\\\"}'\" /tmp/o.txt; cat /tmp/o.txt"
# 4. Установить новую (через PTY — иначе нет вывода)
ssh root@192.168.1.75 "script -q -c \"luna-send -i -f luna://com.webos.appInstallService/dev/install '{\\\"id\\\":\\\"me.wouterdek.magic4pc\\\",\\\"ipkUrl\\\":\\\"/tmp/magic4pc.ipk\\\",\\\"subscribe\\\":true}'\" /tmp/o.txt; cat /tmp/o.txt"
# Ждать "state": "installed" в выводе
# 5. Запустить
ssh root@192.168.1.75 "script -q -c \"luna-send -n 1 -f luna://com.webos.applicationManager/launch '{\\\"id\\\":\\\"me.wouterdek.magic4pc\\\"}'\" /tmp/o.txt; cat /tmp/o.txt"
Проверка логов сервиса
ssh root@192.168.1.75 "script -q -c \"luna-send -n 1 -f luna://me.wouterdek.magic4pc.service/query '{}'\" /tmp/o.txt; cat /tmp/o.txt"
Ожидаемый результат: serviceActive: true, broadcastAdsActive: true (или isConnected: true если HTPC подключён), в логах — listApps: returned N apps.
Деплой Go-клиента на HTPC
cd ~/Developer/magic4pc_altclient
GOOS=linux GOARCH=amd64 go build -o magic4pc_new .
scp magic4pc_new bazzite@192.168.1.86:~/magic4pc/
ssh bazzite@192.168.1.86 "
systemctl --user stop magic4pc
mv ~/magic4pc/magic4pc_new ~/magic4pc/magic4pc
systemctl --user start magic4pc
"
HTPC сервис
ssh bazzite@192.168.1.86 "systemctl --user status magic4pc"
ssh bazzite@192.168.1.86 "systemctl --user restart magic4pc"
enableне используется — autostart через EIM на стороне TV.
Auto-launch при boot/wake
Magic4pc автоматически запускает выбранный app (VK Video, Кинопоиск, last used) при включении TV.
Логика
- Boot (полный ребут) —
/tmpчистится → нет/tmp/magic4pc-run-state→freshStart=true→ запускает app - Wake (standby→on) с последним активным app — init.d при Suspend удаляет
run-state→ при wake нет файла →freshStart=true→ запускает app - Wake после HDMI/EIM source — если последний активный был HDMI (
com.webos.app.hdmi*) — init.d при Suspend тоже удаляетrun-state(TODO: можно улучшить), magic4pc по wake не запускает чужой app если пользователь вручную переключился на HDMI перед сном - Ручной запуск magic4pc (пользователь сам открыл) —
run-state = runningуже есть →freshStart=false→ ничего не запускает ✅
Файлы persistent storage
Сервис (UID 5078, jail) имеет доступ только к /tmp и своей директории:
| Файл | Где | Что |
|---|---|---|
magic4pc-settings |
PERSISTENT_DIR/ |
Выбранный app: appId / __last_used__ / none |
magic4pc-last-app |
PERSISTENT_DIR/ |
Последний foreground app (пишет init.d + сервис) |
magic4pc-run-state |
/tmp/ |
running = уже запускался после boot/wake |
PERSISTENT_DIR = /media/developer/apps/usr/palm/services/me.wouterdek.magic4pc.service
Init.d (root) копирует last-app в PERSISTENT_DIR при каждом обновлении foreground.
Init.d скрипт
Файл на TV: /var/lib/webosbrew/init.d/magic4pc
Исходник в репо: tv-scripts/init.d-magic4pc.sh
Что делает:
- При старте: удаляет
/tmp/magic4pc-run-state(fresh boot) track_foreground— каждые 3 сек пишет foreground app в/tmp/magic4pc-last-appиPERSISTENT_DIR/magic4pc-last-appmonitor_power— при Suspend удаляетrun-stateчтобы wake выглядел как fresh start- Каждые 5 мин обновляет
/tmp/magic4pc-apps.json(список app для Dropdown)
Деплой init.d:
scp tv-scripts/init.d-magic4pc.sh root@192.168.1.75:/var/lib/webosbrew/init.d/magic4pc
ssh root@192.168.1.75 "chmod +x /var/lib/webosbrew/init.d/magic4pc"
# Изменения применяются после ребута TV (init.d запускается Homebrew Channel при старте)
⚠️ Правило: init.d всегда правится локально в репо (
tv-scripts/init.d-magic4pc.sh), затем деплоится на TV черезscp. Никогда не редактировать файл на TV напрямую — изменения не попадут в репо.
Деплой IPK (правильный способ)
cd ~/Developer/magic4pc/webos
./deploy.sh
Скрипт делает всё: build → package → scp → install → launch. Ждёт "state": "installed" и запускает.
⚠️
npm run package=ares-package -n(unsigned) — не использовать, TV отклоняет.
⚠️luna-sendбез PTY (script) молчит — в скрипте уже учтено.
Ручной деплой (если нужно)
# 1. Собрать и упаковать
cd ~/Developer/magic4pc/webos
NODE_OPTIONS=--openssl-legacy-provider npm run build
/Users/admin/webOS_TV_SDK/CLI/bin/ares-package dist/ service/ --outdir .
# 2. Скопировать на TV
scp me.wouterdek.magic4pc_1.1.0_all.ipk root@192.168.1.75:/tmp/magic4pc.ipk
# 3. Установить (через PTY — иначе нет вывода)
ssh root@192.168.1.75 'script -q -c "luna-send -i -f luna://com.webos.appInstallService/dev/install '"'"'{\"id\":\"me.wouterdek.magic4pc\",\"ipkUrl\":\"/tmp/magic4pc.ipk\",\"subscribe\":true}'"'"'" /tmp/o.txt; cat /tmp/o.txt'
# Ждать "state": "installed" в выводе
# 4. Запустить
ssh root@192.168.1.75 'script -q -c "luna-send -n 1 -f luna://com.webos.applicationManager/launch '"'"'{\"id\":\"me.wouterdek.magic4pc\"}'"'"'" /dev/null'
Отладка
# Лог сервиса
ssh root@192.168.1.75 "cat /tmp/m4p_debug.log"
# Текущий выбранный app и last-app
ssh root@192.168.1.75 "cat /media/developer/apps/usr/palm/services/me.wouterdek.magic4pc.service/magic4pc-settings"
ssh root@192.168.1.75 "cat /media/developer/apps/usr/palm/services/me.wouterdek.magic4pc.service/magic4pc-last-app"
# Run-state (есть = уже запускался, нет = fresh start)
ssh root@192.168.1.75 "cat /tmp/magic4pc-run-state"
# Лог init.d
ssh root@192.168.1.75 "cat /tmp/m4p_debug.log | grep 'magic4pc init'"
Известные проблемы
- Guide (458) — перехватывается WebOS системно, EIM-регистрация не помогает. Нет решения.
- listApps —
applicationManager/dev/listAppsнедоступен без dev mode. ИспользуетсяapplicationManager/listApps. - Self-launch — magic4pc не должен запускать сам себя. Фильтруется в
checkAutoLaunchпо appId. - HDMI как last-app — init.d трекает foreground включая HDMI sources. При
__last_used__и последнем активном HDMI — не запустит ничего полезного. Решение: выбрать конкретный app вместоlast_used.