[2026-05-12] taiga sync: family/.DS_Store family/_index.md family/how-to/htpc-access.md family/how-to/htpc-gaming-plans.md family/how-to/htpc-magic4pc.md family/how-to/htpc-migration-plan.md family/how-to/htpc-system.md family/how-to/htpc-webos-luna.md family/how-to/htpc.md family/how-to/magic4pc-webos.md

This commit is contained in:
Local Administrator
2026-05-12 11:02:16 -07:00
commit d25629e140
32 changed files with 3821 additions and 0 deletions
Vendored Executable
BIN
View File
Binary file not shown.
+3
View File
@@ -0,0 +1,3 @@
# Family
[[how-to/]] | [[documents/]] | [[contacts/]] | [[schedule/]]
+67
View File
@@ -0,0 +1,67 @@
# HTPC — Доступ и управление
## SSH
```bash
ssh -i ~/.ssh/id_rsa bazzite@192.168.1.86
```
Пароль sudo: `bazzite`
## Go (на HTPC)
Go установлен в `/usr/local/go/bin/go` (не в PATH по умолчанию).
Сборка: `cd ~/magic4pc && /usr/local/go/bin/go build -o magic4pc_new .`
## Git workflow (magic4pc)
- Mac: `~/Developer/magic4pc_altclient`, бранч `bazzite-linux`
- Remote `linux``ssh://bazzite@192.168.1.86/var/home/bazzite/magic4pc.git` (bare repo)
- Worktree на HTPC: `/home/bazzite/magic4pc`
- Remote `origin``https://github.com/mallexxx/magic4pc_altclient.git`
Деплой:
```bash
# На Mac:
git push linux bazzite-linux
# На HTPC:
cd ~/magic4pc && git pull && /usr/local/go/bin/go build -o magic4pc_new .
sudo systemctl stop magic4pc
sudo rm /usr/local/bin/magic4pc
sudo cp magic4pc_new /usr/local/bin/magic4pc
sudo systemctl start magic4pc
```
> ⚠️ Нельзя `cp` поверх работающего бинаря — "Text file busy". Всегда сначала `stop` → `rm` → `cp` → `start`.
## systemd сервис
Файл: `/etc/systemd/system/magic4pc.service`
```ini
[Unit]
Description=Magic Remote client for LG TV
After=network.target
[Service]
User=bazzite
Group=bazzite
ExecStart=/usr/local/bin/magic4pc-run.sh
Restart=always
RestartSec=5
StartLimitIntervalSec=0
[Install]
WantedBy=multi-user.target
```
`StartLimitIntervalSec=0` — без лимита рестартов (иначе при crash-loop убивается).
## Run script
`/usr/local/bin/magic4pc-run.sh` — ждёт X display, делает `xhost +local:`, запускает бинарь.
## ydotoold
User-юнит: `~/.config/systemd/user/ydotoold.service`
Socket: `/run/user/1000/.ydotool_socket`
Управление: `systemctl --user status ydotoold`
## Логи
```bash
journalctl -u magic4pc -n 50 --no-pager
journalctl -u magic4pc -f # live
```
+65
View File
@@ -0,0 +1,65 @@
# HTPC — Планы: Игры и Эмуляторы
## Статус системы
- [x] Bazzite установлен, автологин в Gaming Mode
- [x] 1080p@60Hz, аудио LPCM работает
- [x] LG Magic Remote работает (magic4pc)
- [x] Без пароля при входе
- [ ] EmuDeck
- [ ] Proton-GE
- [ ] MangoHud
- [ ] Эмуляторы
## Следующие шаги
### 1. EmuDeck
Установка через официальный скрипт:
```bash
# В KDE Desktop Mode (не gamescope):
curl -L https://raw.githubusercontent.com/dragoonDorise/EmuDeck/main/install.sh | bash
```
Настроит RetroArch, PCSX2, Yuzu/Ryujinx и др. автоматически.
ROM-и класть в `~/Emulation/roms/<system>/`
### 2. Proton-GE
Через ProtonUp-Qt (устанавливается из Discover или Flatpak):
```bash
flatpak install flathub net.davidotek.pupgui2
```
Или через EmuDeck — он включает ProtonUp-Qt.
### 3. MangoHud (FPS overlay)
Уже есть в Bazzite. Включить для игры:
- В Steam: Properties → Launch Options: `mangohud %command%`
- Глобально: `MANGOHUD=1` в environment
### 4. Эмуляторы (план)
| Платформа | Эмулятор | Статус |
|-----------|----------|--------|
| NES | RetroArch (Nestopia) | Через EmuDeck |
| SNES | RetroArch (bsnes/Snes9x) | Через EmuDeck |
| Sega Mega Drive | RetroArch (Genesis Plus GX) | Через EmuDeck |
| PS2 | PCSX2 | Через EmuDeck |
| N64 | RetroArch (Mupen64Plus) | Через EmuDeck |
| Switch | Ryujinx (или Suyu) | Через EmuDeck |
### 5. ROM-и
Скопировать в `~/Emulation/roms/` по папкам после установки EmuDeck.
EmuDeck сам создаст структуру директорий.
### 6. Steam ROM Manager
Входит в EmuDeck — добавляет эмуляторные игры в Steam библиотеку с обложками.
После добавления они появятся в Gaming Mode как обычные Steam игры.
## Железо — ограничения
- RX 570: Switch эмуляция может быть медленной (Vulkan поддерживается)
- i7-6800K (Broadwell-E): PS2 и Switch — проверить производительность
- 32GB RAM: достаточно для всего
## Управление в эмуляторах
Magic Remote (magic4pc) работает как мышь + клавиатура.
Для эмуляторов нужен геймпад — варианты:
- Bluetooth геймпад (PS4/PS5/Xbox)
- USB геймпад
- Настроить маппинг Magic Remote кнопок на gamepad через Steam Input (если эмулятор в Steam)
+60
View File
@@ -0,0 +1,60 @@
# magic4pc — LG Magic Remote для HTPC
## Что это
Go-клиент для LG Magic Remote (webOS приложение на TV).
Форк: https://github.com/mallexxx/magic4pc_altclient (бранч `bazzite-linux`)
Оригинал: https://github.com/netham45/magic4pc_altclient
## Архитектура
- **TV сервер**: webOS приложение на LG TV (192.168.1.75:42831), шлёт UDP
- **HTPC клиент**: `/usr/local/bin/magic4pc`, UDP-клиент на фиксированном source port **9106**
- **xdotool**: единый persistent процесс через stdin pipe — мышь и клавиши
- **ydotool**: для gamescope-специфичных команд (Ctrl+1, Ctrl+2) через `/run/user/1000/.ydotool_socket`
## Ключевые фиксы (история)
| Проблема | Решение |
|----------|---------|
| TV "service error" / не реконнектится | Фиксированный source UDP port 9106 (`ListenUDP(":9106")`) |
| TV зависает в "waiting for client" | Server keepalive timeout 10s → auto-reconnect |
| KDE XTest диалог при каждом клике | Единый xdotool процесс (не новый на каждую кнопку) |
| KDE XTest диалог при рестарте xdotool | `XwaylandEisNoPrompt=true` в kwinrc |
| Кривые координаты мыши | Динамический scale через `xdotool getdisplaygeometry` |
| Крестик слал Ctrl/a/s/d | TV keycodes 37-40 → `Left/Up/Right/Down` keysym |
| Цифры слали мусор | ASCII keycodes → `string(rune(key))` |
## Маппинг кнопок
| Кнопка | Keycode | Gaming Mode (gamescope) | KDE Desktop |
|--------|---------|------------------------|-------------|
| Красная | 403 | Ctrl+1 (Steam меню) | Super (Win) |
| Зелёная | 404 | Escape | Escape |
| Жёлтая | 405 | Ctrl+2 (Steam QAM) | Средняя кнопка мыши |
| Синяя | 406 | Правый клик | Правый клик |
| Back | 461 | Mouse X1 (back) | Mouse X1 (back) |
| Enter/OK | 13 | Return | Return |
| Ch Up | 33 | PageUp | PageUp |
| Ch Down | 34 | PageDown | PageDown |
| D-pad Left | 37 | Left | Left |
| D-pad Up | 38 | Up | Up |
| D-pad Right | 39 | Right | Right |
| D-pad Down | 40 | Down | Down |
| Play | 415 | XF86AudioPlay | XF86AudioPlay |
| Stop | 413 | XF86AudioStop | XF86AudioStop |
| Pause | 19 | XF86AudioPause | XF86AudioPause |
| Цифры 0-9 | 48-57 | 0-9 | 0-9 |
| GUIDE | 458 | Правый клик | Правый клик |
| Мышь (тач) | — | mousemove (scaled) | mousemove (scaled) |
| Тап | mousedown/up | Left click | Left click |
| Скролл | wheel | scroll 4/5 | scroll 4/5 |
## Определение сессии
`isGamescopeSession()` — читает `/proc/*/cmdline`, ищет `kwin_wayland`. Если нет → gamescope.
## Koordinаты мыши
TV шлёт координаты в пространстве 0–1920 × 0–1080.
При старте xdotool запрашивается `xdotool getdisplaygeometry` → вычисляется scale.
Работает корректно при любом разрешении экрана.
## Проблемы которые могут вернуться
- **xdotool умирает** (write error) → KDE показывает XTest диалог. Разрешить и включить `XwaylandEisNoPrompt=true`.
- **TV "service error"** — если висит намертво, cold reboot TV (выдернуть из розетки).
- **Бинарь слетает** после `rpm-ostree upgrade` → пересобрать и скопировать.
+171
View File
@@ -0,0 +1,171 @@
# HTPC Migration Plan
> Создано: 2026-05-08. Цель: перенести всё с Windows раздела, настроить эмуляторы, бэкап.
## Статус
- [x] magic4pc установлен и работает (main)
- [x] Кнопка назад → Esc в gamescope / x1 в KDE
- [ ] EmuDeck установлен (в процессе)
- [ ] Эмуляторы настроены
- [ ] ROMs добавлены в Steam
- [ ] Сейвы перенесены
- [ ] Игры с Data/Windows добавлены в Steam
- [ ] Бэкап системы
- [ ] Удаление Windows раздела
---
## 1. EmuDeck — после установки визарда
### Симлинки ROMs (папки с пробелами → структура EmuDeck)
```bash
ln -s "/run/media/bazzite/Data/n64 roms" ~/Emulation/roms/n64
ln -s "/run/media/bazzite/Data/snes roms" ~/Emulation/roms/snes
ln -s "/run/media/bazzite/Data/gba roms" ~/Emulation/roms/gba
ln -s "/run/media/bazzite/Data/3ds roms" ~/Emulation/roms/3ds
ln -s "/run/media/bazzite/Data/ps2 roms" ~/Emulation/roms/ps2
ln -s "/run/media/bazzite/Data/dolphin roms" ~/Emulation/roms/wii
ln -s "/run/media/bazzite/Data/cemu roms" ~/Emulation/roms/wiiu
ln -s "/run/media/bazzite/Data/mame roms" ~/Emulation/roms/mame
ln -s "/run/media/bazzite/Data/sega roms" ~/Emulation/roms/genesis
ln -s "/run/media/bazzite/Data/nes" ~/Emulation/roms/nes
ln -s "/run/media/bazzite/Data/xbox" ~/Emulation/roms/xbox # Metal Slug 4
```
- [ ] Разобрать дубли: `mame roms` + `mame_roms`, `n64` + `n64 roms`
- [ ] Разобрать `Data/unsorted/` — есть PC игры (Skyrim, Portal 2, Rayman и др.) и PS2 (Odin Sphere)
---
## 2. Эмуляторы — настройка и конфигурация
### Уже установлено EmuDeck:
- [x] RetroArch (`org.libretro.RetroArch`)
- [x] Dolphin (`org.DolphinEmu.dolphin-emu`)
- [x] PPSSPP (`org.ppsspp.PPSSPP`)
- [x] melonDS (`net.kuribo64.melonDS`) — NDS
### Нужно установить/проверить:
- [ ] **Ryujinx или Yuzu-fork** (Switch) — EmuDeck должен предложить, проверить после визарда
- Switch ROMs: пока не найдены на Data (папки nsp/xci нет) — уточнить у Алекса есть ли ромы
- [ ] **PCSX2** (PS2) — проверить установил ли EmuDeck
- [ ] **Citra / Lime3DS** (3DS) — проверить
- [ ] **CEMU** (Wii U) — проверить (есть cemu roms на Data: DKC Tropical Freeze и др.)
### Конфигурация по эмуляторам:
- [ ] **Dolphin** — настроить контроллер (LG Magic Remote), проверить Wii Motion Plus
- [ ] **RetroArch** — настроить cores для каждой платформы, hotkeys
- [ ] **PPSSPP** — PSP, настроить разрешение upscale для TV
- [ ] **CEMU** — Wii U, настроить графику (Vulkan для RX570)
- [ ] **Ryujinx/Switch** — prod.keys, title.keys (нужны с реальной консоли)
- [ ] **PCSX2** — PS2 BIOS (нужен)
- [ ] **melonDS** — NDS BIOS/firmware
---
## 3. Добавление игр в Steam
### Steam ROM Manager (EmuDeck)
- [ ] Запустить из Desktop Mode после настройки симлинков
- [ ] Включить парсеры для: SNES, N64, GBA, NDS, Wii, Wii U, PS2, PSP, 3DS, Switch, MAME
- [ ] Проверить обложки, исправить неправильно распознанные
### Игры с Data — добавить в Steam вручную как Non-Steam games
**`/run/media/bazzite/Data/games/`** (38 игр, уже установлены):
- Bayonetta, Cuphead, GTA V, Fallout 4, Metro Exodus, The Witcher 3, Rayman Legends/Origins, Castle Crashers, Brawlhalla, Duck Game, Human Fall Flat, Pummel Party и др.
**`/run/media/bazzite/Data/steam/steamapps/common/`** (уже Steam игры):
- Gang Beasts, Shovel Knight, Stick Fight, Party Animals, Them's Fightin' Herds, Unravel и др.
- [ ] Переместить/добавить эту steamapps папку как вторую Steam library
### Игры с Windows раздела — перенести на Data
Папки которые стоит проверить и перенести если нужно:
- [ ] Проверить `Windows/Users/mallexxx/AppData/Local/` на наличие установленных игр
- [ ] `Data/unsorted/` — Skyrim SE, Portal 2, Rise of Tomb Raider, Rocket League, Ori — решить: установить через Steam или перенести
---
## 4. Перенос сейвов (приоритет высокий)
### RetroArch
Источник: `Windows/Users/mallexxx/AppData/Roaming/RetroArch/`
Назначение: `~/.var/app/org.libretro.RetroArch/config/retroarch/`
- [ ] `saves/` — 14 .srm: Zelda ALTTP, DKC2, DKC3, Banjo-Tooie, SM64, Contra Hard Corps и др.
- [ ] `states/` — Aladdin, DKC2, Contra, Golden Axe и др.
### OpenEmu → RetroArch
Источник: `/run/media/bazzite/Data/openemu/`
- [ ] `snes9x/battery saves/*.sav` — Chrono Trigger, Lufia, Brain Lord, Dragon View, Actraiser, Zelda ALTTP (~30+ игр)
- [ ] `save states/` — GB, GBA, NDS, NES, SNES
- Формат `.oesavestate` — уточнить конвертер (возможно совместим с RetroArch напрямую)
### Dolphin
Источник: `Windows/Users/mallexxx/AppData/Roaming/Dolphin Emulator/`
Назначение: `~/.var/app/org.DolphinEmu.dolphin-emu/data/dolphin-emu/`
- [ ] `GC/USA/`, `GC/EUR/`, `GC/JAP/` — Memory Card
- [ ] `Wii/` — NAND (пустой, сейвы Wii утеряны)
### Citra (3DS)
- [ ] `AppData/Roaming/Citra/``~/.var/app/org.citra_emu.citra/`
### С NAS
- [ ] `mallexxx.duckdns.org:/mnt/RED_2TB/backup/game/dkc3 103perc save.zip` — DKC3 103% ⭐
- [ ] `…/backup/win_2020/…/RetroArch/saves/` — Mortal Kombat II state (нет на Windows)
### DKC Returns
- ❌ Сейвы Wii **утеряны** нигде не найдены. ROM: `Data/dolphin roms/donkey kong country returns.iso`
---
## 5. Личные файлы с Windows раздела
- [ ] `Documents/основание_контроллер_кормушки.SLDPRT` + `сборка…` → NAS
- [ ] `Documents/DAVAProject/` — разобраться
- [ ] `Documents/AutoHotkey.ahk` + `Desktop/unbind-guide-button.ps1` → сохранить
- [ ] `Documents/JoyToKey/` — конфиги
- [ ] `AppData/Roaming/Kodi/userdata/``~/.kodi/userdata/`
---
## 6. Медиа
- [ ] `Data/видеокассеты-наташа/` → NAS (семейное видео, приоритет!)
- [ ] `Data/мультфильмы/` + `Data/сериалы/` → сравнить с NAS `storage/Cartoons/`, убрать дубли
- [ ] `Data/movies/` → сравнить с NAS `storage/Movies/`
---
## 7. Бэкап системы
- [ ] Btrfs snapshots → NAS (`btrbk` или `snapper`)
- [ ] `~/Emulation/saves/` rsync → NAS по расписанию
- [ ] Проверить automount Data в Gaming Mode (gamescope)
---
## 8. Удаление Windows раздела (~230GB)
Условия готовности:
- [ ] Все сейвы перенесены и проверены в игре
- [ ] Все личные файлы на NAS/Linux
- [ ] Kodi настроен
- [ ] Бэкап работает
```bash
# fdisk /dev/sdb → удалить раздел → расширить Data или Linux
```
---
## Справка — пути эмуляторов на Bazzite (EmuDeck Flatpak)
| Эмулятор | Saves path |
|----------|-----------|
| RetroArch | `~/.var/app/org.libretro.RetroArch/config/retroarch/saves/` |
| Dolphin | `~/.var/app/org.DolphinEmu.dolphin-emu/data/dolphin-emu/` |
| Citra/Lime3DS | `~/.var/app/org.citra_emu.citra/` |
| PCSX2 | `~/.var/app/net.pcsx2.PCSX2/config/PCSX2/memcards/` |
| RPCS3 | `~/.var/app/net.rpcs3.RPCS3/config/rpcs3/dev_hdd0/` |
| PPSSPP | `~/.var/app/org.ppsspp.PPSSPP/config/ppsspp/PSP/SAVEDATA/` |
| melonDS | `~/.var/app/net.kuribo64.melonDS/` |
| Ryujinx | `~/.var/app/org.ryujinx.Ryujinx/config/Ryujinx/bis/user/save/` |
+111
View File
@@ -0,0 +1,111 @@
# HTPC — Система (Bazzite Linux)
## Железо
- CPU: Intel Core i7-6800K
- RAM: 32 GB
- GPU: AMD Radeon RX 570
- Подключение к TV: HDMI
- TV: LG webOS (192.168.1.75)
- HTPC IP: 192.168.1.86
## ОС
- **Bazzite 44** (Fedora-based, immutable OSTree)
- Вариант: `bazzite-deck` (Steam Gaming Mode / gamescope)
- Пользователь: `bazzite`
- Hostname: `htpc`
- DNS: резолвится как `htpc` через роутер (192.168.1.1), см. [[router-bishkek-asus]]
## Режимы запуска
- **Gaming Mode (gamescope-session-steam)** — автологин по умолчанию, Steam Big Picture
- **KDE Desktop** — переключение из меню Steam → "Switch to Desktop"
## Автологин
Файл: `/etc/gdm/custom.conf`
```ini
[daemon]
AutomaticLoginEnable=True
AutomaticLogin=bazzite
Session=gamescope-session-steam
```
## Переменные окружения
`/etc/environment`:
```
GAMESCOPE_DISABLE_HDR=1
```
`~/.config/environment.d/gamescope.conf`:
```
GAMESCOPE_WIDTH=1920
GAMESCOPE_HEIGHT=1080
GAMESCOPE_REFRESH=60
```
## Разрешение
- **1080p @ 60Hz** — рабочий режим
- 4K вызывает AMD HDA audio clock bug: пиксельный клок ~594 MHz → HDMI audio 880 Hz вместо 440 Hz
## Аудио
### Проблема
LG webOS TV по HDMI принимает **только LPCM 2ch**.
Поддерживаемые частоты: 32000 / 44100 / 48000 / 96000 / 192000 Hz, 16/20/24 bit.
**AMD HDA audio clock bug** (RX 570 + 4K@60Hz):
При pixel clock ~594 MHz (4K@60Hz) AMD HDMI audio контроллер выдаёт неправильный clock — тон 440 Hz воспроизводится как 880 Hz (в 2 раза выше). Решение — переключиться на **1080p@60Hz** (pixel clock ~148 MHz, bug не воспроизводится).
### Решение: зафиксировать 1080p
`~/.config/environment.d/gamescope.conf`:
```
GAMESCOPE_WIDTH=1920
GAMESCOPE_HEIGHT=1080
GAMESCOPE_REFRESH=60
```
### Решение: PipeWire/WirePlumber конфиги
`~/.config/wireplumber/wireplumber.conf.d/fix-hdmi.conf`:
```lua
monitor.alsa.rules = [
{
matches = [{ node.name = "~alsa_output.pci*hdmi*" }]
actions = {
update-props = {
audio.format = "S16LE"
audio.rate = 48000
session.suspend-timeout-seconds = 0
}
}
}
]
```
`~/.config/pipewire/pipewire.conf.d/99-hdmi-fix.conf`:
```conf
context.properties = {
default.clock.allowed-rates = [ 44100 48000 ]
}
```
### Проверка
```bash
# Посмотреть активный sink и его параметры:
pactl list sinks | grep -A20 "HDMI"
# Тест тона 440 Hz — должен звучать как ля первой октавы:
speaker-test -t sine -f 440 -c 2
```
## OSTree / Обновления
- `/usr/local/bin` слетает при `rpm-ostree` upgrade (новый `/usr` overlay)
- Бинари держать в `/home/bazzite/` или пересобирать после обновлений
- Проверить после апдейта: `systemctl status magic4pc`
## KDE Xwayland настройки
`~/.config/kwinrc`:
```ini
[Xwayland]
EavesdropMode=2
XwaylandEisNoPrompt=true
```
`XwaylandEisNoPrompt=true` — отключает диалог разрешения XTest (нужен для magic4pc/xdotool).
+44
View File
@@ -0,0 +1,44 @@
# WebOS TV — luna-send via SSH
TV IP: `192.168.1.75`, user: `root`
## Проблема
`luna-send` пишет вывод в tty, не в stdout. Через SSH без PTY — пусто.
## Решение — через `script`
```bash
ssh root@192.168.1.75 "script -q -c \"luna-send -n 1 -f luna://...\" /tmp/o.txt; cat /tmp/o.txt"
```
## Методы
### Список запущенных приложений
```bash
ssh root@192.168.1.75 "script -q -c \"luna-send -n 1 -f luna://com.webos.service.applicationmanager/running '{}'\" /tmp/o.txt; cat /tmp/o.txt"
```
### Закрыть приложение
```bash
ssh root@192.168.1.75 "script -q -c \"luna-send -n 1 -f luna://com.webos.service.applicationmanager/closeByAppId '{\"id\":\"APP_ID\"}'\" /tmp/o.txt; cat /tmp/o.txt"
```
### Удалить приложение (dev)
```bash
ssh root@192.168.1.75 "script -q -c \"luna-send -n 1 -f luna://com.webos.appInstallService/dev/remove '{\"id\":\"APP_ID\"}'\" /tmp/o.txt; cat /tmp/o.txt"
```
### Установить ipk
```bash
scp app.ipk root@192.168.1.75:/tmp/
ssh root@192.168.1.75 "script -q -c \"luna-send -n 1 -f luna://com.webos.appInstallService/dev/install '{\"id\":\"APP_ID\",\"ipkUrl\":\"/tmp/app.ipk\",\"subscribe\":true}'\" /tmp/o.txt; cat /tmp/o.txt"
```
### Запустить приложение
```bash
ssh root@192.168.1.75 "script -q -c \"luna-send -n 1 -f luna://com.webos.applicationManager/launch '{\"id\":\"APP_ID\"}'\" /tmp/o.txt; cat /tmp/o.txt"
```
## Не работает
- `ares-install/ares-launch` — требует dev mode (порт 9922)
- `listRunningApps` — нет метода, правильно: `running`
- `applicationManager/remove` — нет, правильно: `appInstallService/dev/remove`
+57
View File
@@ -0,0 +1,57 @@
# HTPC
**IP:** 192.168.1.86
**OS:** Bazzite (Linux)
## Wake on LAN
**MAC:** `B0:6E:BF:60:6D:64`
### Скрипты (~/Automation/)
```bash
# Разбудить HTPC
python3 ~/Automation/wake-on-lan.py
# Разбудить или усыпить TV + HTPC
python3 ~/Automation/wake-tv-or-sleep.py
```
### Вручную (без скриптов)
```bash
# через brew
brew install wakeonlan
wakeonlan B0:6E:BF:60:6D:64
# через Python
python3 -c "
import socket
mac = 'B06EBF606D64'
pkt = bytes.fromhex('F'*12 + mac*16)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.sendto(pkt, ('255.255.255.255', 9))
print('Magic packet sent')
"
```
### Проверка
После отправки пакета подождать ~30 сек:
```bash
ping -c 5 192.168.1.86
```
### Если не просыпается
WoL должен быть включён в BIOS/UEFI. Проверить настройки на HTPC (пока он живой):
```bash
sudo ethtool enp<X> # смотреть Wake-on: g
sudo ethtool -s enp<X> wol g # включить
```
## Виртуальная клавиатура
Установлен **vboard** (fork mallexxx/vboard, ветка `htpc-bazzite-patches`).
Полная дока с патчами и известными проблемами: [[virtual-keyboard-bazzite]]
+222
View File
@@ -0,0 +1,222 @@
# 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`)
## Исходники
| Компонент | Путь |
|-----------|------|
| 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 автоматически при каждой сборке:
```js
// config/webpack.config.js
'process.env.BUILD_DATE': JSON.stringify(new Date().toISOString().slice(0, 16).replace('T', ' '))
```
## Сборка WebOS app
```bash
cd ~/Developer/magic4pc/webos
NODE_OPTIONS=--openssl-legacy-provider npm run build
```
> ⚠️ Требует `--openssl-legacy-provider` — старый webpack несовместим с Node 25.
## Сборка IPK
```bash
/Users/admin/webOS_TV_SDK/CLI/bin/ares-package dist/ service/ --outdir .
```
> ⚠️ Оба аргумента `dist/ service/` обязательны — иначе сервис не попадёт в IPK.
> ⚠️ `npm run package` = `ares-package -n` (unsigned) — **не использовать**, TV отклоняет.
## Деплой на TV (192.168.1.75)
### Правильный способ — deploy.sh
```bash
cd ~/Developer/magic4pc/webos
./deploy.sh
```
Скрипт делает всё: build → package → scp → close → remove → install → launch.
Использует `~/Automation/tv-luna-send.sh` для close/remove/launch.
Для install (`-i` subscribe) — прямой ssh+script с polling на `"state":"installed"`.
> ⚠️ **Не использовать `ares-install` / `ares-launch`** — не ждут завершения, работают ненадёжно.
> ⚠️ `luna-send` без PTY молчит — см. [[tv-luna-send]].
<details><summary>Ручной деплой</summary>
```bash
cd ~/Developer/magic4pc/webos
NODE_OPTIONS=--openssl-legacy-provider npm run build
/Users/admin/webOS_TV_SDK/CLI/bin/ares-package dist/ service/ --outdir .
scp me.wouterdek.magic4pc_1.1.0_all.ipk root@192.168.1.75:/tmp/magic4pc.ipk
~/Automation/tv-luna-send.sh -n 1 -f luna://com.webos.service.applicationmanager/closeByAppId '{"id":"me.wouterdek.magic4pc"}' || true
~/Automation/tv-luna-send.sh -n 1 -f luna://com.webos.appInstallService/dev/remove '{"id":"me.wouterdek.magic4pc"}' || true
sleep 2
# install — через прямой script (luna-send -i не работает через враппер)
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/install.txt &
for i in \$(seq 1 30); do sleep 1; grep -q '\"state\":\"installed\"' /tmp/install.txt 2>/dev/null && break; done
kill %1 2>/dev/null; grep -v '^Script ' /tmp/install.txt; rm -f /tmp/install.txt"
~/Automation/tv-luna-send.sh -n 1 -f luna://com.webos.applicationManager/launch '{"id":"me.wouterdek.magic4pc"}'
```
</details>
### Проверка статуса сервиса
```bash
~/Automation/tv-luna-send.sh -n 1 -f luna://me.wouterdek.magic4pc.service/query '{}'
```
Ожидаемый результат: `serviceActive: true`, `broadcastAdsActive: true`.
## Деплой Go-клиента на HTPC
```bash
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 сервис
```bash
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 при включении TV.
### Логика
- **Boot** — `/tmp` чистится → нет `run-state``freshStart=true` → запускает app
- **Wake** — init.d при Suspend удаляет `run-state` → при wake `freshStart=true` → запускает app
- **Ручной запуск** — `run-state=running` уже есть → `freshStart=false` → ничего не запускает ✅
### Файлы persistent storage
`PERSISTENT_DIR = /media/developer/apps/usr/palm/services/me.wouterdek.magic4pc.service`
| Файл | Где | Что |
|------|-----|-----|
| `magic4pc-settings` | `PERSISTENT_DIR/` | Выбранный app: appId / `__last_used__` / `none` |
| `magic4pc-last-app` | `PERSISTENT_DIR/` | Последний foreground app |
| `magic4pc-run-state` | `/tmp/` | `running` = уже запускался после boot/wake |
### Init.d скрипт
Файл на TV: `/var/lib/webosbrew/init.d/magic4pc`
Исходник: `tv-scripts/init.d-magic4pc.sh` в репо
```bash
# Деплой
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
```
> ⚠️ Всегда править в репо, не на TV напрямую.
## Отладка
```bash
# Лог UI (appendLogPersist пишет сюда)
ssh root@192.168.1.75 "tail -30 /tmp/m4p_debug.log"
# Run-state
ssh root@192.168.1.75 "cat /tmp/magic4pc-run-state"
# Settings / last-app
ssh root@192.168.1.75 "cat $PERSISTENT_DIR/magic4pc-settings"
ssh root@192.168.1.75 "cat $PERSISTENT_DIR/magic4pc-last-app"
```
> ⚠️ `appendLog()` пишет **только в UI**. В файл попадает только `appendLogPersist()`. При отладке использовать `appendLogPersist`.
## Back и клавиатура — особенности WebOS
**Как работает Back когда открыта системная клавиатура:**
- **Back #1** — система поглощает полностью: закрывает клавиатуру, вызывает `Input.onClose`. DOM `keydown` **не приходит**, `onButtonDown` **не вызывается**.
- **Back #2** — приходит в DOM → `onButtonDown`.
**Следствие:** нельзя детектировать первый Back через `keydown`. Единственный способ — флаг в `onActivate`.
**Реализованный фикс** (`_kbWasOpen` в `MainPanel.js`):
```js
// Input.onActivate:
this._kbWasOpen = true;
// onButtonDown при Back:
if (this.state.wolMacActive || this._kbWasOpen) {
this._kbWasOpen = false;
return; // не закрывать панель
}
```
**Что не работает на WebOS:**
- `Popup.noAutoDismiss` — Back закрывает через `onButtonDown`, не через Popup.onClose
- Таймаут (`Date.now() < ts + 1000ms`) — ненадёжно
- Polling `document.activeElement` — фокус никогда не переходит на `INPUT`, клавиатура — системный overlay
## Известные проблемы
- **Guide (458)** — перехватывается WebOS системно. Нет решения.
- **listApps** — `dev/listApps` недоступен без dev mode. Используется `applicationManager/listApps`.
- **Self-launch** — фильтруется в `checkAutoLaunch` по appId.
- **HDMI как last-app** — при `__last_used__` и последнем активном HDMI не запустит ничего полезного. Решение: выбрать конкретный app.
- **`ares-install` ненадёжен** — молчит без PTY, не ждёт завершения. Использовать `luna-send dev/install` через script.
+29
View File
@@ -0,0 +1,29 @@
# Музыкальный плеер с TrueNAS на Android
**Статус:** 📋 Планируется
**Дата:** 2026-05-10
## Задача
Поднять стриминг музыки с TrueNAS (`/mnt/RED_2TB/storage/Music/`) на Android-устройства.
## Варианты
### ✅ Вариант 1 — Navidrome + Symfonium (рекомендован)
- **Сервер:** [Navidrome](https://www.navidrome.org/) — лёгкий музыкальный сервер, Docker-образ
- **Плеер Android:** [Symfonium](https://symfonium.app/) — Subsonic API, офлайн-кэш, теги, обложки
- Работает через интернет по порту, можно за nginx reverse proxy
- Docker уже есть: `/mnt/RED_2TB/docker/`
### Вариант 2 — Jellyfin + Finamp
- **Сервер:** Jellyfin (Docker) — универсален (видео, фото, музыка)
- **Плеер Android:** [Finamp](https://github.com/jmshrv/finamp) — опенсорс
- Чуть тяжелее Navidrome
### Вариант 3 — SMB напрямую
- Без сервера, VLC или Solid Explorer играют по SMB
- Только для домашней сети — по интернету медленно и небезопасно
## Следующие шаги
- [ ] Поднять Navidrome в Docker на TrueNAS
- [ ] Пробросить порт / настроить nginx proxy
- [ ] Установить Symfonium на Android, подключить
+336
View File
@@ -0,0 +1,336 @@
---
tags:
- raspberry-pi
- nas
- openmediavault
- how-to
created: '2026-05-11'
updated: '2026-05-12'
status: hermes-pending
ip: 192.168.1.14
---
# OpenMediaVault на Raspberry Pi 5
**Статус:** Контейнеры запущены, Time Machine pending (май 2026)
**Цель:** NAS на RPi 5 — OS на SD-карте, хранилище на USB 3.0 HDD
## Прогресс
| Шаг | Статус | Заметки |
|-----|--------|---------|
| 1 — Флашим SD | ✅ | Bookworm 2025-05-13, RPi Imager |
| 2 — Первый запуск + обновление | ✅ | apt upgrade выполнен |
| 3 — Установка OMV | ✅ | OMV 7 установлен |
| 4 — Первый вход в веб-интерфейс | ✅ | Пароль сменён |
| 5 — Сеть | ✅ | DHCP static lease `192.168.1.14` (eth0 MAC `2c:cf:67:64:03:f3`); WiFi отключён (modprobe blacklist brcmfmac/brcmutil) |
| 6 — USB HDD | ✅ | 12TB Seagate ST12000NT001, EXT4, UUID `49e8f586-3839-4c5d-a1e1-58bfc3579ade`, смонтирован через OMV в `/srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade` |
| 7 — omv-extras + Docker | ✅ | omv-extras 7.0.6, Docker 29.4.3, kraken в группе docker |
| 8 — Контейнеры | ✅ | Все через отдельные docker compose, конфиги на HDD (см. ниже) |
| 9 — Time Machine share | ✅ | `mbentley/timemachine:smb`, расписание 10:00-22:00 GMT+6, HDD/timemachine |
| 10 — Перенос данных | 🔲 | |
| 11 — Hermes агент Кракен | 🚧 | Dockerfile + config готовы, нужно: заполнить .env, HA token, собрать образ |
| 12 — Obsidian sync (Taiga↔Kraken↔Eagle) | 🔲 | Настроить синхронизацию vault между всеми тремя агентами |
---
## Железо
- Raspberry Pi 5 (4 или 8 GB RAM)
- SD-карта ≥16 GB (Samsung/SanDisk A1 Class 10+)
- USB 3.0 HDD для хранения данных
- Проводной Ethernet (обязателен)
---
## Шаг 1 — Флашим SD-карту
**Образ:** Raspberry Pi OS Lite **64-bit (Bookworm / Debian 12)**
> ⚠️ Брать именно Bookworm, не Trixie (Debian 13) — OMV не поддерживается на Trixie!
- Последний Bookworm: **2025-05-13**
- Прямая ссылка: https://downloads.raspberrypi.com/raspios_lite_arm64/images/raspios_lite_arm64-2025-05-13/2025-05-13-raspios-bookworm-arm64-lite.img.xz
**Инструмент:** Raspberry Pi Imager (https://www.raspberrypi.com/software/)
> ⚠️ Не Balena Etcher — только RPi Imager поддерживает OS Customisation!
> При выборе образа из каталога (не custom file) появляется кнопка "Edit Settings"
**Настройки (Edit Settings → шестерёнка):**
- Hostname: `kraken`
- Username: `kraken` (не `admin` — зарезервировано для OMV!)
- Password: сохранён в 1Password (запись "Kraken RPi")
- SSH: Enable → Allow public-key authentication → вставить `~/.ssh/id_rsa.pub`
После флаша вставить карту в Pi и включить.
---
## Шаг 2 — Первый запуск, обновление и настройка сети
SSH подключение (пока через WiFi/роутер):
```bash
ssh -i ~/.ssh/id_rsa kraken@kraken.local
```
Обновление системы:
```bash
sudo apt-get update && sudo apt-get upgrade -y
```
Static IP на eth0 (прямой кабель Mac ↔ Pi для установки):
```bash
sudo nmcli con add type ethernet ifname eth0 con-name eth0-static \
ipv4.method manual \
ipv4.addresses 192.168.100.2/24 \
ipv4.gateway 192.168.100.1 \
ipv4.never-default yes \
connection.autoconnect yes
sudo nmcli con up eth0-static
```
> ⚠️ `ipv4.never-default yes` — обязательно! Иначе интернет пойдёт через Mac, а не WiFi, и установка зависнет.
---
## Шаг 3 — Установка OMV
```bash
# Pre-install
wget -qO- https://github.com/OpenMediaVault-Plugin-Developers/installScript/raw/master/preinstall | sudo bash
sudo reboot
# После перезагрузки — основная установка (~30 мин)
wget -qO- https://github.com/OpenMediaVault-Plugin-Developers/installScript/raw/master/install | sudo bash
```
Pi перезагрузится автоматически по окончании.
---
## Шаг 4 — Первый вход в веб-интерфейс
- URL: `http://192.168.100.2` (или `http://kraken.local`)
- Логин: `admin`
- Пароль: `openmediavault`
- **Сразу сменить пароль!** (System → General Settings → Web Administrator Password)
---
## Шаг 5 — Сеть: DHCP + static lease, отключить WiFi
Переключить кабель Pi → роутер. Настроить DHCP через netplan (OMV управляет `/etc/netplan/20-openmediavault-eth0.yaml`):
```yaml
network:
ethernets:
eth0:
match:
macaddress: "2c:cf:67:64:03:f3"
dhcp4: yes
dhcp6: no
link-local: []
```
В роутере (ASUS): добавить static lease в `dhcp_staticlist` через nvram:
```bash
ssh -i ~/.ssh/id_rsa admin@192.168.1.1 \
"nvram set dhcp_staticlist='...<2C:CF:67:64:03:F3>192.168.1.14>kraken>' && nvram commit && service restart_dnsmasq"
```
Отключить WiFi навсегда (modprobe blacklist):
```bash
sudo sh -c 'echo blacklist brcmfmac > /etc/modprobe.d/disable-wifi.conf' < /dev/null
sudo sh -c 'echo blacklist brcmutil >> /etc/modprobe.d/disable-wifi.conf' < /dev/null
sudo update-initramfs -u -k all < /dev/null
```
> ⚠️ Все sudo команды через SSH требуют `< /dev/null` — иначе sudo ждёт stdin и зависает
---
## Шаг 6 — USB HDD
1. Подключить HDD к синему (USB 3.0) порту Pi
2. Проверить скорость: `sudo apt install hdparm && sudo hdparm -t --direct /dev/sda` → должно быть ~200-250 MB/s
3. Разметить: `sudo parted -s /dev/sda mklabel gpt mkpart primary 0% 100%`
4. Форматировать (агент не может — только вручную или через скрипт):
```bash
sudo bash /tmp/fmt.sh # скрипт с mkfs.ext4 -L storage /dev/sda1
```
5. OMV Web UI → Storage → File Systems → выбрать `/dev/sda1` → Mount
6. OMV монтирует в: `/srv/dev-disk-by-uuid-<UUID>/`
> ⚠️ Не монтировать вручную через fstab — OMV должен управлять монтированием сам
---
## Шаг 7 — omv-extras + Docker
```bash
# omv-extras
wget -qO- https://github.com/OpenMediaVault-Plugin-Developers/packages/raw/master/install | sudo bash < /dev/null
# Docker
curl -fsSL https://get.docker.com -o /tmp/get-docker.sh && sudo sh /tmp/get-docker.sh < /dev/null
sudo usermod -aG docker kraken < /dev/null
```
---
## Шаг 8 — Docker контейнеры
Каждый сервис — отдельный `docker-compose.yml` в `/home/kraken/<name>/`.
Все конфиги на HDD: `/srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/docker/<name>/`
| Сервис | Compose | URL | Данные |
|--------|---------|-----|--------|
| Portainer | `/home/kraken/portainer/` | :9000 | HDD/docker/portainer |
| Watchtower | `/home/kraken/watchtower/` | — | stateless |
| Home Assistant | `/home/kraken/homeassistant/` | :8123 | HDD/docker/homeassistant |
| Jellyfin | `/home/kraken/media/` | :8096 | HDD/docker/jellyfin, media:ro |
| Transmission | `/home/kraken/media/` | :9091 | HDD/docker/transmission, HDD/media/downloads |
| Rclone | `/home/kraken/rclone/` | — | cron воскр. 03:00 → mailru-crypt:kraken/docker |
Управление:
```bash
cd /home/kraken/<name> && sudo docker compose up -d # запустить
cd /home/kraken/<name> && sudo docker compose restart # рестарт
cd /home/kraken/<name> && sudo docker compose pull && sudo docker compose up -d # обновить
```
> ⚠️ Watchtower: нужна переменная `DOCKER_API_VERSION=1.54` — иначе крашится (API mismatch)
> ⚠️ sudo через SSH требует `< /dev/null` для каждой команды с tee/pipe
---
## Шаг 9 — Time Machine share
Контейнер: `mbentley/timemachine:smb`
Compose: `/home/kraken/timemachine/docker-compose.yml`
Данные: `/srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/timemachine/`
**Расписание** (crontab kraken-пользователя):
- `0 4 * * *` UTC (10:00 GMT+6) → `/usr/local/bin/tm-start.sh` — поднять контейнер
- `0 16 * * *` UTC (22:00 GMT+6) → `/usr/local/bin/tm-stop.sh` — остановить контейнер
- `restart: no` — при загрузке Pi не стартует автоматически
**Credentials:**
- Пользователь: `timemachine`
- Пароль: `timemachine` ⚠️ сменить позже
**Подключение с Mac:**
1. Finder → `⌘K` → `smb://192.168.1.14`
2. Логин: `timemachine` / `timemachine`
3. System Preferences → Time Machine → Add Disk → выбрать `kraken`
Лог cron: `/var/log/timemachine-cron.log`
---
## Шаг 10 — Перенос данных
### Фильмы с Mac на NAS
```bash
rsync -avh --progress /path/to/movies/ kraken@kraken.local:/srv/.../media/movies/
```
### Time Machine
> ⚠️ Старый TM-бэкап перенести нельзя напрямую — TM привязывает бэкап к конкретному диску/хосту.
> Начать новый бэкап на NAS, старый HDD хранить как архив или отформатировать после нескольких успешных новых бэкапов.
---
## Шаг 11 — Hermes агент Кракен
### Файлы
```
/home/kraken/hermes/
Dockerfile — образ на базе node:20-slim + hermes-agent + obsidian-mcp
docker-compose.yml — restart: unless-stopped, network_mode: host
.env — API ключи (заполнить вручную)
config.yaml → копируется в /srv/.../docker/hermes/config/config.yaml
```
### Модели (проверено 2026-05-12)
Бесплатные модели с tool calling на OpenRouter:
| Модель | Статус |
|--------|--------|
| `nvidia/nemotron-3-super-120b-a12b:free` | ✅ работает |
| `openrouter/free` | ✅ fallback (авто-роутинг) |
| `deepseek/deepseek-chat-v3-0324:free` | ❌ 404 |
| `qwen/qwen3-235b-a22b:free` | ❌ 404 |
| `google/gemma-4-31b-it:free` | ❌ 429 rate limit |
**Primary**: `nvidia/nemotron-3-super-120b-a12b:free` (262K ctx, tools ✅)
**Auxiliary/Fallback**: `openrouter/free`
**Рекомендуется**: добавить `GOOGLE_API_KEY` → Gemini Flash как auxiliary
### Telegram бот
- Имя: Кракен
- Bot: [@kraken_htpc_bot](https://t.me/kraken_htpc_bot)
- Token: `8750114596:AAH625OBSdb_Bn7ZSwshjDD3mbENnkSRYag`
### Toolsets
`hermes-cli, terminal, file, web, web_crawler, memory, spotify, homeassistant`
### Деплой (после заполнения .env)
```bash
# Заполнить ключи:
nano /home/kraken/hermes/.env
# OPENROUTER_API_KEY=sk-or-...
# GOOGLE_API_KEY=...
# HASS_TOKEN=... (создать в HA UI → Profile → Long-Lived Access Tokens)
# Собрать и запустить:
cd /home/kraken/hermes
sudo docker compose build
sudo docker compose up -d
# Проверить:
sudo docker logs hermes-kraken --tail 20
```
### TODO
- [ ] Заполнить .env (OPENROUTER_API_KEY, GOOGLE_API_KEY)
- [ ] Настроить HA → создать Long-Lived token → вписать в HASS_TOKEN
- [ ] Собрать и запустить контейнер
- [ ] Настроить obsidian-mcp (vault path = /vault)
- [ ] Перенести Spotify auth с Eagle (или запарить заново)
---
## Шаг 12 — Obsidian sync (Taiga ↔ Kraken ↔ Eagle)
Все три агента должны работать с одним vault. Варианты:
- **Git sync** — vault в git repo, каждый агент делает pull/push
- **rsync** — периодическая синхронизация через SSH
- **Syncthing** — p2p sync без облака
Текущее состояние vault:
- Eagle (Mac): `~/obsidian/` — git repo, auto-commit 23:00
- Taiga (TrueNAS): `/mnt/RED_2TB/storage/obsidian/` — read-only mount
- Kraken (RPi): `/vault` — пока не подключён
**Следующие шаги:**
- [ ] Определить схему sync (git vs syncthing vs rsync)
- [ ] Настроить write-доступ на Тайге и Кракене
- [ ] Автоматический pull на всех агентах при изменениях
---
## Уроки (что пошло не так при установке)
- **Balena Etcher** не поддерживает OS Customisation — использовать только **RPi Imager**
- **`userconf.txt`** deprecated на Bookworm — правильный способ через RPi Imager (он пишет `custom.toml`)
- **`systemd.run=` в cmdline.txt** нужно указывать `/usr/lib/raspberrypi-sys-mods/firstboot`, а не свой скрипт
- **`ipv4.never-default yes`** при настройке static IP на eth0 — обязательно, иначе интернет идёт через Mac и установка зависает
- **OMV install script** при зависании — скорее всего нет интернета, проверить маршруты (`ip route`)
---
## Полезные ссылки
- Официальный вики OMV: https://wiki.omv-extras.org/
- Install script: https://github.com/OpenMediaVault-Plugin-Developers/installScript
- RPi Imager: https://www.raspberrypi.com/software/
- RPi Downloads: https://www.raspberrypi.com/software/operating-systems/
+84
View File
@@ -0,0 +1,84 @@
# Роутер Бишкек — ASUS RT-AC68U
## Основное
| Параметр | Значение |
|----------|----------|
| Модель | ASUS RT-AC68U |
| IP | 192.168.1.1 |
| Login | admin |
| SSH | включён (dropbear) |
| SSH auth | ключ `~/.ssh/id_rsa` (mallexxx@Alexeys-MBP) |
## SSH доступ
```bash
ssh -i ~/.ssh/id_rsa admin@192.168.1.1
```
Ключ хранится в `/home/root/.ssh/authorized_keys` на роутере.
## DNS конфигурация
### Upstream DNS (WAN)
- Primary: `94.140.14.14` (AdGuard DNS)
- Secondary: `94.140.15.15` (AdGuard DNS)
### DHCP DNS для клиентов
Клиенты получают IP роутера (`192.168.1.1`) как единственный DNS-сервер.
Это позволяет dnsmasq на роутере резолвить локальные имена.
> ⚠️ **Было**: `dhcp_dns1_x=4.4.4.4` — клиенты шли напрямую в интернет, локальные имена не резолвились. Убрано 2026-05-10.
## Локальные DNS имена
Настроено в `/jffs/configs/dnsmasq.conf.add`:
```
# Static local hostnames
address=/htpc/192.168.1.86
address=/tv/192.168.1.75
```
Динамические имена из DHCP leases резолвятся автоматически (Mac, MacBookPro, LGwebOSTV и др.).
## Устройства в сети
| Имя | IP | MAC | Примечание |
|-----|----|-----|------------|
| htpc | 192.168.1.86 | — | Bazzite, статический IP, RX 570 |
| tv / LGwebOSTV | 192.168.1.75 | ac:5a:f0:25:11:c6 | LG TV |
| Mac | 192.168.1.57 | 5a:fe:e7:bb:b3:d1 | |
| MacBookPro | 192.168.1.43 | 76:88:0d:b9:6a:cd | |
| zhimi-airpurifier (1) | 192.168.1.58 | b4:04:29:38:6a:63 | Xiaomi очиститель воздуха |
| zhimi-airpurifier (2) | 192.168.1.59 | 64:90:c1:f2:cb:13 | Xiaomi очиститель воздуха |
| Galaxy-S23 | 192.168.1.96 | 3a:2f:09:6e:49:1e | |
## Полезные команды
```bash
# Проверить DNS резолвинг локальных имён
dig @192.168.1.1 htpc +short
dig @192.168.1.1 tv +short
# Посмотреть DHCP leases
ssh -i ~/.ssh/id_rsa admin@192.168.1.1 "cat /tmp/dnsmasq.leases"
# Перезапустить dnsmasq
ssh -i ~/.ssh/id_rsa admin@192.168.1.1 "service restart_dnsmasq"
# Показать nvram DNS настройки
ssh -i ~/.ssh/id_rsa admin@192.168.1.1 "nvram show | grep -E '^(dhcp_dns|wan_dns|wan0_dns)'"
```
## Кастомный dnsmasq
Файл: `/jffs/configs/dnsmasq.conf.add`
```
address=/snu.lge.com.usgcac.cdnetworks.net/snu.lge.com/su.lge.com/192.168.1.1
# Static local hostnames
address=/htpc/192.168.1.86
address=/tv/192.168.1.75
```
+49
View File
@@ -0,0 +1,49 @@
# Музыка на SD-карту колонки
*Обновлено: 2026-05-12*
## Рабочие папки (Mac)
- `~/Downloads/music-merged/` — итоговая папка для записи на SD (309+ треков, 26+ папок)
- `~/Downloads/music-sd/` — новые треки подборки (не было на SD)
- `~/Downloads/music-sd-backup-2026-05-12/` — бэкап оригинального содержимого SD
- `~/Downloads/music/` — полная коллекция (~156 треков, не все идут на SD)
## Структура на SD
Папка `Детские/` — советские мультяшные и детские песни.
Все остальные артисты — **отдельные папки**, даже с 1 треком.
## Что входит в подборку
| Артист | Кол-во | Примечания |
|--------|--------|------------|
| Король и Шут | 75 | Полный набор (старый SD + новые) |
| КняZz | 18 | Все скачанные |
| Детские | 160+ | Советские мультяшные |
| Агата Кристи | 7 | Без: Сердцебиение, Секрет, Пуля, Нисхождение, Декаданс, В интересах революции |
| Наутилус Помпилиус | 5 | |
| Высоцкий | 6 | Сказочные/приключенческие + Козёл отпущения |
| Ляпис Трубецкой | 6 | Студийные версии (не концертные) |
| Жуки | 3 | Батарейка, Танкист, Влечение |
| Любэ | 3 | Конь, Там за туманами, Скворцы |
| Сектор Газа | 4 | Лирика, Туман + Демобилизация, Пора домой |
| Саундтреки | 2 | He's a Pirate, Harry Potter Main Theme |
| Тамара Миансарова | 1 | Чёрный кот (оригинал 1963, не Браво!) |
| Андрей Миронов | 3 | Бабочка, Белеет мой парус, Остров невезения |
| Пионерлагерь Пыльная Радуга | 2 | Бемби, Дорога добра |
| Little Big | 1 | Skibidi |
| Серьга | 1 | Живёт моя отрада |
| Ундервуд | 1 | Вдоль по питерской |
| Руки вверх | 2 | |
| Green Apelsin | 2 | |
| Чичерина | 2 | |
| Юрий Никулин | 3 | |
| Хабиб | 1 | Ягода малинка |
| Женя Трофимов | 1 | Первая Любовь |
| Plamenev | 3 | |
## Правила сортировки
- Из `Детские/` **выносить** в отдельные папки: Сектор Газа, Серьга, Ундервуд, Андрей Миронов, Пионерлагерь Пыльная Радуга, Little Big, Руки вверх
- Файлы формата `Артист - Название.mp3` → разбивать по папкам автоматически
- КиШ: убирать префикс "Король и Шут - " из имён файлов
## Что НЕ вошло (есть в ~/Downloads/music/)
КняZz (все вошли), Классика, Муслим Магомаев, Мельница, Ария, Iron Maiden, Jethro Tull, Машина Времени, Пикник, Аквариум, Би-2, ДДТ, Чайф, Сплин, Визбор, Окуджава, Камбурова, Мгзавреби, Слава КПСС, Otava Yo, The Hatters, Несчастный случай, Калинов Мост, Канцлер Ги
+44
View File
@@ -0,0 +1,44 @@
# TrueNAS — Домашний NAS
## Основное
| Параметр | Значение |
|----------|------------------------------------|
| Версия | TrueNAS SCALE 24.10 |
| Web UI | https://mallexxx.duckdns.org |
| Web UI (IP) | http://90.189.160.148 |
| SSH | truenas_admin@mallexxx.duckdns.org |
| SSH (IP) | truenas_admin@90.189.160.148 |
| SSH-ключ | ~/.ssh/id_rsa |
## Подключение
```bash
ssh -i ~/.ssh/id_rsa truenas_admin@mallexxx.duckdns.org
```
## Пул и датасеты
Пул: RED_2TB (ZFS)
Точка монтирования: /mnt/RED_2TB/
| Датасет | Путь | Назначение |
|---------------|-------------------------------|-----------------------|
| storage/ | /mnt/RED_2TB/storage/ | Общее хранилище |
| backup/ | /mnt/RED_2TB/backup/ | Резервные копии |
| Photos/ | /mnt/RED_2TB/Photos/ | Фотографии |
| TimeMachine/ | /mnt/RED_2TB/TimeMachine/ | Бэкапы macOS |
| Edu/ | /mnt/RED_2TB/Edu/ | Учебные материалы |
| storage/git/ | /mnt/RED_2TB/storage/git/ | Git-репозитории |
## ⚠️ Важно: boot-pool не выживает после обновления
/home и /root находятся на boot-pool и **стираются при обновлении TrueNAS**.
Всё важное (конфиги, ключи, скрипты) должно лежать в /mnt/RED_2TB/,
а не в /home/truenas_admin/ или /root/.
Типичные жертвы:
- SSH authorized_keys в /home
- Cron jobs, написанные в /etc
- Любые файлы вне /mnt/RED_2TB/
+118
View File
@@ -0,0 +1,118 @@
# TrueNAS — инфраструктура
> Обновлено: 2026-04-30
## Доступ
- **Host (local):** `192.168.2.197`
- **Host (external):** `mallexxx.duckdns.org` (DuckDNS, иногда слетает резолв — fallback на 192.168.2.197)
- **SSH:** `truenas_admin@mallexxx.duckdns.org -i ~/.ssh/id_rsa`
- **Web UI:** http://truenas.mallexxx.duckdns.org
- **Portainer:** https://portainer.mallexxx.duckdns.org
## Пользователи и группы (ключевые)
| uid | Имя | gid | Назначение |
|-----|-----|-----|------------|
| 950 | truenas_admin | 950 | SSH-пользователь, управление docker |
| 911 | transdamon | 911 | Процесс Transmission внутри контейнера |
| 921 | transmission | 921 | Старый пользователь (не используется контейнером) |
| 3000 | nas_users | 3000 | Общая группа доступа к NAS |
## Структура папок
```
/mnt/RED_2TB/
├── docker/ ← конфиги docker-контейнеров (бэкапятся → mailru-crypt:)
│ ├── caddy/
│ ├── filebrowser/
│ ├── ha/ ← Home Assistant
│ ├── hermes/ ← Hermes-Taiga
│ ├── homeassistant/
│ ├── immich/
│ ├── inpx-web/
│ ├── inpxer/
│ ├── mbusd/
│ ├── modbus-bridge/
│ ├── mosquitto/
│ ├── nodered/
│ ├── portainer/
│ ├── python/
│ ├── rclone/
│ ├── ser2net/
│ ├── transmission/ ← конфиг Transmission (settings.json, torrents, resume...)
│ ├── vless-proxy/
│ ├── watchtower/
│ ├── webdav/
│ └── zigbee2mqtt/
├── storage/
│ ├── Downloads/ ← данные торрентов (монтируется в контейнер как /mnt/storage)
│ │ ├── transmission/ ← старый путь конфига (больше не используется)
│ │ └── [медиафайлы]
│ └── obsidian/ ← vault (read-only для hermes-taiga)
├── backup/ ← ручные бэкапы (бэкапятся → mailru-crypt:)
│ └── transmission-config/ ← снапшот конфига от 2026-04-30
├── Photos/ ← фото (бэкапятся → mailru:Photos/Photos)
├── old-bu/ ← старый архив (бэкапятся → mailru:Photos/old-bu)
└── immich-photos-upload/ ← библиотека Immich (бэкапятся → mailru:Photos/immich)
```
## NFSv4 ACL — важно
TrueNAS использует **NFSv4 ACL**, а не стандартный POSIX chmod/chown.
- `ls -la` показывает `----------` даже если ACL есть → всегда проверять через `midclt call filesystem.getacl <path>`
- Добавить запись: `midclt call filesystem.setacl '{...}'`
- `truenas_admin` не имеет passwordless sudo → root-операции только через midclt или TrueNAS UI
## Docker-контейнеры
| Контейнер | Image | Порт | Домен |
|-----------|-------|------|-------|
| transmission | linuxserver/transmission:latest | 9091, 51413 | transmission.mallexxx.duckdns.org |
| hermes-taiga | hermes-taiga:latest | — | — |
| vless-proxy | teddysun/xray:latest | — | — |
| filebrowser | filebrowser/filebrowser:latest | — | — |
| webdav | hacdias/webdav:latest | — | webdav.mallexxx.duckdns.org |
| homeassistant | home-assistant:stable | 8123 | mallexxx.duckdns.org |
| immich-server | immich-app/immich-server:release | 2283 | immich.mallexxx.duckdns.org |
| immich-postgres | tensorchord/pgvecto-rs:pg14-v0.2.0 | — | — |
| immich-redis | redis:7 | — | — |
| nodered | nodered/node-red:latest | 1880 | nodered.mallexxx.duckdns.org |
| mosquitto | eclipse-mosquitto:latest | — | — |
| caddy | caddy:latest | 80, 443 | reverse proxy для всего |
| rclone | rclone/rclone:latest | — | бэкап по cron |
| portainer | portainer/portainer-ce:latest | 9000 | portainer.mallexxx.duckdns.org |
| watchtower | containrrr/watchtower:latest | — | авто-обновление образов |
| inpxer | ghcr.io/hedger/inpxer:latest | 18080 | books.mallexxx.duckdns.org |
| nodered | node-red:latest | 1880 | nodered.mallexxx.duckdns.org |
| zigbee2mqtt | koenkk/zigbee2mqtt:latest | — | — |
| mbusd | 3cky/mbusd:latest | — | Modbus |
| modbus-bridge | modbus-bridge | — | — |
| cups-splix | cups-splix | — | принтер |
### Transmission — детали
- **Config:** `/mnt/RED_2TB/docker/transmission/` (монтируется как `/config`)
- **Данные:** `/mnt/RED_2TB/storage` (монтируется как `/mnt/storage`)
- **Download dir:** `/mnt/storage/Downloads`
- **UID/GID:** 911/911 (пользователь `transdamon`)
- **RPC whitelist:** `172.16.6.*`
- **Web:** https://transmission.mallexxx.duckdns.org
- **Порты:** 9091 (RPC/web), 51413 (TCP/UDP peers)
### Caddy — домены
| Домен | → |
|-------|---|
| mallexxx.duckdns.org | Home Assistant :8123 |
| transmission.mallexxx.duckdns.org | Transmission :9091 |
| immich.mallexxx.duckdns.org | Immich :2283 |
| nodered.mallexxx.duckdns.org | Node-RED :1880 |
| webdav.mallexxx.duckdns.org | WebDAV |
| books.mallexxx.duckdns.org | Inpxer :18080 |
| portainer.mallexxx.duckdns.org | Portainer :9000 |
| truenas.mallexxx.duckdns.org | TrueNAS UI :80 |
| cam.mallexxx.duckdns.org | Камера :8090 |
| docs.mallexxx.duckdns.org | Docs :8000 |
## Связанные заметки
- [[truenas-access]] — SSH-доступ
- [[truenas-rclone-backup]] — система бэкапов
+48
View File
@@ -0,0 +1,48 @@
# TrueNAS — rclone backup
## Контейнер
- Image: `rclone/rclone:latest`
- Имя: `rclone`
- Запуск: cron внутри контейнера (`crond -f`)
## Маппинги
| Host | Контейнер |
|------|-----------|
| `/mnt/RED_2TB/docker/rclone/config` | `/config/rclone` |
| `/mnt/RED_2TB` | `/data` |
## Расписание
```
0 3 * * 0 # каждое воскресенье в 03:00
```
Скрипт: `/config/rclone/backup.sh`
## Что бэкапится
| Метка | Источник (host) | Назначение | Шифрование |
|-------|-----------------|------------|------------|
| Photos | `/mnt/RED_2TB/Photos` | `mailru:Photos/Photos` | нет |
| old-bu | `/mnt/RED_2TB/old-bu` | `mailru:Photos/old-bu` | нет |
| immich | `/mnt/RED_2TB/immich-photos-upload` | `mailru:Photos/immich` | нет |
| docker | `/mnt/RED_2TB/docker` | `mailru-crypt:` | ✅ да |
| backup | `/mnt/RED_2TB/backup` | `mailru-crypt:` | ✅ да |
## Remotes
- **mailru** — WebDAV, Mail.ru Cloud (`webdav.cloud.mail.ru`), user: `mallexxx@mail.ru`
- **mailru-crypt** — rclone crypt поверх `mailru:backup`, шифрование имён файлов и директорий (`standard`)
## Добавить что-то в бэкап
Шифрованное → положить в `/mnt/RED_2TB/backup/` или `/mnt/RED_2TB/docker/` — подхватится автоматически.
Нешифрованное → добавить `run_sync` строку в `/mnt/RED_2TB/docker/rclone/config/backup.sh`.
## Файлы конфигурации
- `/mnt/RED_2TB/docker/rclone/config/rclone.conf` — remotes (root-only)
- `/mnt/RED_2TB/docker/rclone/config/crontab` — расписание
- `/mnt/RED_2TB/docker/rclone/config/backup.sh` — скрипт
+49
View File
@@ -0,0 +1,49 @@
# tv-luna-send.sh — враппер для luna-send на LG TV
Скрипт: `~/Automation/tv-luna-send.sh`
## Проблема
`luna-send` на WebOS через SSH **не выводит результат в stdout** без PTY.
- `ssh root@TV "luna-send -n 1 ..."` — молчит
- `ssh -t root@TV "luna-send -n 1 ..."` — тоже молчит
- `script -q -c "luna-send ..." /tmp/out.txt`**работает**
Причина: luna-send детектирует TTY и отказывается писать в pipe.
## Решение
`script(1)` аллоцирует псевдо-TTY и пишет вывод в файл. Враппер запускает luna-send через script, читает tmpfile (уникальный через $$), фильтрует мусор, удаляет файл.
## Использование
```bash
~/Automation/tv-luna-send.sh [luna-send flags] luna://service/method 'json'
```
Примеры:
```bash
# foreground app
~/Automation/tv-luna-send.sh -n 1 -f luna://com.webos.applicationManager/getForegroundAppInfo '{}'
# закрыть app
~/Automation/tv-luna-send.sh -n 1 -f luna://com.webos.service.applicationmanager/closeByAppId '{"id":"me.wouterdek.magic4pc"}'
# запустить app
~/Automation/tv-luna-send.sh -n 1 -f luna://com.webos.applicationManager/launch '{"id":"me.wouterdek.magic4pc"}'
# статус сервиса
~/Automation/tv-luna-send.sh -n 1 -f luna://me.wouterdek.magic4pc.service/query '{}'
```
Переменная `TV_HOST` переопределяет таргет (default: `root@LGwebOSTV`, alias `root@tv`, IP: `root@192.168.1.75`).
DNS резолвится через роутер (192.168.1.1), см. [[router-bishkek-asus]].
## Ограничение
`luna-send -i` (subscribe, бесконечный) не работает через враппер. Для install (`dev/install`) используется прямой ssh+script с polling в deploy.sh.
## Где используется
`~/Developer/magic4pc/webos/deploy.sh` — close, remove, launch.
+172
View File
@@ -0,0 +1,172 @@
# Виртуальная клавиатура на HTPC (Bazzite KDE Wayland)
Связано: [[htpc]] | [[magic4pc-webos]]
## Контекст
**HTPC:** [[htpc]] (192.168.1.86, Bazzite, KDE Plasma 6, Wayland)
Задача: экранная клавиатура для редкого ввода текста без физической клавиатуры.
---
## Изученные варианты
### ❌ Maliit (официальная KDE клавиатура)
- Единственный officially supported OSK для KDE Wayland
- **Критический баг (upstream, не Bazzite):** появляется один раз, после скрытия повторно не открывается без `pkill maliit-keyboard && maliit-keyboard`
- На Bazzite desktop: опция Virtual Keyboard вообще отсутствует в Accessibility Settings
- Статус: **не пригоден для HTPC**
### ❌ Onboard
- Полнофункциональная клавиатура с modifier keys
- Работает через XWayland + uinput (`GDK_BACKEND=x11 onboard`)
- Не rpm-ostree пакет, требует `rpm-ostree install onboard` (layered package)
- Нет трея, нет интеграции с KDE Virtual Keyboard API
- Статус: **рабочий fallback, но хуже vboard**
### ✅ vboard (выбранное решение)
- Форк: [archisman-panigrahi/vboard](https://github.com/archisman-panigrahi/vboard) (оригинал mdev588/vboard заброшен/заархивирован)
- Наш форк с патчами: [mallexxx/vboard](https://github.com/mallexxx/vboard) ветка `htpc-bazzite-patches`
- Python + GTK3 + uinput backend
- Нативный Wayland на KDE Plasma, трей, регистрируется как Virtual Keyboard
---
## Установка на Bazzite
### Зависимости (pip, без rpm-ostree)
```bash
pip3 install python-uinput meson ninja
```
> `/dev/uinput` уже доступен через Steam ACL (`user:bazzite:rw-` через `steam-devices` udev rules) — sudo не нужен.
### Сборка и установка (user scope)
```bash
git clone https://github.com/mallexxx/vboard.git
cd vboard
git checkout htpc-bazzite-patches
meson setup builddir-user --prefix=$HOME/.local
meson compile -C builddir-user
meson install -C builddir-user
# Создаёт KWin rule автоматически
```
### Регистрация как KDE Virtual Keyboard
```bash
kwriteconfig6 --file kwinrc --group Plugins --key kwin_wayland_vkbd_plugin 'io.github.archisman-panigrahi.vboard'
kwriteconfig6 --file kwinrc --group Plugins --key VirtualKeyboardEnabled true
```
Проверить: System Settings → Input Devices → Virtual Keyboard → выбрать Vboard.
---
## Известные проблемы и решения
### ⚠️ Ввод не работает (NullInputBackend)
**Причина:** `python-uinput` из pip не имеет `KEY_LESS` (есть только в системном пакете).
UInputBackend падает при инициализации → fallback на NullInputBackend → нажатия игнорируются.
**Патч** (`vboard/input_backends.py`):
```python
# было:
"<": uinput.KEY_LESS,
# стало:
"<": uinput.KEY_102ND,
```
`KEY_102ND` — стандартный keycode для клавиши `<`/`>` на европейских раскладках, есть в pip-версии.
### ⚠️ Автопоказ/скрытие по фокусу поля не работает
**Причина:** vboard не реализует `zwp_input_method_v2` Wayland протокол.
KDE запускает его как процесс, но сигналы "show/hide" не передаются.
**Решение:** оставить как есть — клавиатура висит поверх, управление через трей.
### ⚠️ Русская раскладка вводит латиницу
**Причина:** vboard шлёт физические keycodes (KEY_Q и т.д.), система переводит по своей раскладке.
**Решение:** синхронизировать системную раскладку KDE через D-Bus при переключении (см. патчи ниже).
### ⚠️ Таскбар исчез при попытке закрепить иконку через kwriteconfig
**Причина:** неверный group path в `plasma-org.kde.plasma.desktop-appletsrc` → plasmashell упал.
**Решение:** `plasmashell --replace` через SSH.
**Правильный способ** закрепить иконку: правый клик на `^` → Configure System Tray → Entries → Virtual Keyboard → Always Shown.
---
## Кастомные патчи (ветка htpc-bazzite-patches)
### 1. Скрыть кнопку закрытия
`vboard/window.py`:
```python
# было:
self.header.set_show_close_button(True)
# стало:
self.header.set_show_close_button(False)
```
### 2. Кнопка переключения EN/RU в хедере
`vboard/window.py` — в `create_settings()` после ESC кнопки:
```python
self.lang_button = Gtk.Button(label=self.keyboard_layout.upper())
self.lang_button.connect("clicked", self.cycle_keyboard_layout)
self.lang_button.set_name("esc-button")
self.header.pack_start(self.lang_button)
```
Новые методы:
```python
def cycle_keyboard_layout(self, widget=None):
keys = [k for k, _ in KEY_LAYOUT_CHOICES]
idx = keys.index(self.keyboard_layout) if self.keyboard_layout in keys else 0
next_key = keys[(idx + 1) % len(keys)]
self.set_keyboard_layout(next_key)
self.lang_button.set_label(next_key.upper())
self.sync_system_layout(next_key)
def sync_system_layout(self, layout_key):
import subprocess
layout_map = {"en": "0", "ru": "1"}
index = layout_map.get(layout_key)
if index is None:
return
try:
subprocess.Popen([
"qdbus", "org.kde.keyboard", "/Layouts",
"org.kde.KeyboardLayouts.setLayout", index
], env=dict(__import__("os").environ,
DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/1000/bus"))
except Exception:
pass
```
> Индексы `en=0, ru=1` соответствуют порядку раскладок в KDE: проверить через
> `qdbus --literal org.kde.keyboard /Layouts org.kde.KeyboardLayouts.getLayoutsList`
### 3. Только EN/RU раскладки
`vboard/constants.py`:
```python
KEY_LAYOUT_CHOICES = (
("en", "English (US)"),
("ru", "Russian"),
)
```
---
## Управление
```bash
# Запустить вручную (обычно KDE делает сам)
XDG_RUNTIME_DIR=/run/user/1000 WAYLAND_DISPLAY=wayland-0 \
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus \
python3 ~/.local/bin/vboard &
# Перезапустить
kill -9 $(pgrep -f vboard)
# KDE перезапустит автоматически через ~3 сек
# Проверить что backend работает
python3 -c "
import sys; sys.path.insert(0, '$HOME/.local/share/vboard')
from vboard.input_backends import UInputBackend
b = UInputBackend(); print('OK')
"
```