167 lines
8.0 KiB
Markdown
167 lines
8.0 KiB
Markdown
# ambilight-plasmoid
|
||
|
||
Ambilight per KDE Plasma 6: cattura il bordo superiore dello schermo su **Wayland**
|
||
(portal ScreenCast + PipeWire), calcola il colore medio di 47 zone e pilota una
|
||
striscia **WS2812B** collegata a un **ESP8266 (Wemos D1 mini)** con firmware
|
||
**Adalight** via seriale. Il controllo sta in un **plasmoide nel vassoio di sistema**,
|
||
con popup per luminosità, contrasto, saturazione e banda campionata.
|
||
|
||
```
|
||
schermo ──portal──▶ ambilightd ──Adalight/seriale──▶ ESP8266 ──▶ striscia
|
||
▲
|
||
│ D-Bus org.enne2.Ambilight
|
||
│
|
||
ambilight-ctl ◀── plasmoide (Plasma 6, QML)
|
||
```
|
||
|
||
## Componenti
|
||
|
||
| File | Ruolo |
|
||
|---|---|
|
||
| `backend/ambicapture.py` | negoziazione ScreenCast portal + pipeline GStreamer (`pipewiresrc`) |
|
||
| `backend/ambilightd.py` | demone: cattura, calcolo zone, luminosità/contrasto/saturazione, invio Adalight, servizio D-Bus |
|
||
| `backend/ambilight-ctl` | CLI che parla col demone e stampa JSON (usata dal plasmoide) |
|
||
| `backend/ambilight.service` | unit `systemd --user` (avvio automatico al login) |
|
||
| `package/org.enne2.ambilight/` | plasmoide Plasma 6 (icona nel vassoio + popup con slider) |
|
||
| `install.sh` / `uninstall.sh` | installazione e rimozione, senza root |
|
||
|
||
## Hardware
|
||
|
||
- ESP8266 Wemos D1 mini (clone), firmware **Adalight-FastLED** con `PIN_DATA 5` (= D1/GPIO5),
|
||
`Num_Leds` = dimensione buffer (256): il numero reale di LED lo decide il software che invia i frame.
|
||
- Striscia WS2812B **47 LED** sul solo bordo superiore.
|
||
- Cablaggio: `D1 → 470 Ω → DIN`, alimentatore 5 V separato (~60 mA/LED), **massa comune**,
|
||
1000 µF sull'alimentazione; level shifter 74AHCT125 se il filo dati è lungo.
|
||
|
||
## Installazione
|
||
|
||
```sh
|
||
./install.sh # backend + servizio utente + plasmoide
|
||
```
|
||
|
||
Al primo avvio il desktop chiede il permesso di **condividere lo schermo**: va concesso
|
||
una volta (viene memorizzato in `~/.cache/ambilight/restore_token`).
|
||
|
||
Il plasmoide appare nel vassoio come **Ambilight** (icona TV); clic per il popup.
|
||
|
||
## Calibrazione del bianco
|
||
|
||
Dal popup del plasmoide scegli **Calibra…**: la fascia superiore bianca permette di
|
||
regolare in tempo reale i gain RGB e la luminosità. Le variazioni vengono salvate
|
||
nel file di configurazione dal demone; **Esporta profilo JSON…** ne salva una copia
|
||
separata. Chiudi la finestra con il pulsante **Chiudi**. Richiede PySide6.
|
||
|
||
## Uso da terminale
|
||
|
||
```sh
|
||
ambilight-ctl state # JSON con tutto lo stato
|
||
ambilight-ctl start | stop | toggle
|
||
ambilight-ctl set brightness 0.8 # brightness|contrast|saturation|band|reverse|leds
|
||
ambilight-ctl get fps
|
||
systemctl --user status ambilight.service
|
||
```
|
||
|
||
## Configurazione
|
||
|
||
`~/.config/ambilight/config.json` (scritto dal demone a ogni modifica):
|
||
|
||
| chiave | range | default | note |
|
||
|---|---|---|---|
|
||
| `brightness` | 0.05 – 2.0 | 1.0 | moltiplicatore |
|
||
| `contrast` | 0.5 – 2.0 | 1.0 | pivot sul grigio 128 |
|
||
| `saturation` | 0.0 – 2.0 | 1.0 | 0 = bianco e nero |
|
||
| `band` | 0.02 – 0.5 | 0.12 | frazione di altezza campionata |
|
||
| `reverse` | bool | true | LED 0 sul lato destro (verificato a schermo) |
|
||
| `leds` | 1 – 256 | 47 | LED della striscia |
|
||
| `port`, `baud` | – | `/dev/ttyUSB0`, 115200 | seriale |
|
||
| `side` | top/bottom | top | bordo campionato |
|
||
| `auto_start` | bool | true | streaming attivo all'avvio del demone |
|
||
|
||
## Prestazioni misurate
|
||
|
||
- 47 LED, Adalight a 115200: ~**78 fps** (limite teorico della seriale per 147 byte/frame).
|
||
- Cattura: portal + PipeWire, pipeline ridotta a `47×4 × 72` px RGB → costo CPU trascurabile.
|
||
|
||
|
||
## Nome stabile del dispositivo seriale
|
||
|
||
La scheda (CH340, `1a86:7523`) **non espone un iSerial**: verificato con
|
||
`cat /sys/bus/usb/devices/1-2/serial` → vuoto. Di conseguenza:
|
||
|
||
| percorso | valore su questa macchina | unico? |
|
||
|---|---|---|
|
||
| `/dev/ttyUSB0` | assegnato in ordine di enumerazione | no |
|
||
| `/dev/serial/by-id/usb-1a86_USB_Serial-if00-port0` | derivato da `ID_SERIAL=1a86_USB_Serial` | **no** (due cloni identici collidono) |
|
||
| `/dev/serial/by-path/pci-0000:65:00.3-usb-0:2:1.0-port0` | legato alla porta USB fisica | sì |
|
||
| `/dev/ambilight` (regola udev) | symlink creato da noi | sì |
|
||
|
||
> **Su questa macchina la regola NON è installata**: scelta esplicita dell'utente di
|
||
> lasciare `ttyUSB0` come assegnato dal kernel. I file restano nel repo come opzione.
|
||
|
||
Installa la regola (richiede sudo, una volta):
|
||
|
||
```sh
|
||
sudo ./setup-udev.sh /dev/ttyUSB0 # rileva la porta fisica e scrive /etc/udev/rules.d/99-ambilight.rules
|
||
ambilight-ctl set port /dev/ambilight
|
||
systemctl --user restart ambilight.service
|
||
```
|
||
|
||
La regola aggancia `KERNELS=="<porta>"` + VID/PID (non il numero `ttyUSB`), crea
|
||
`/dev/ambilight`, aggiunge `ID_MM_DEVICE_IGNORE` (ModemManager smette di sondare la
|
||
porta) e `TAG+="uaccess"` (accesso ACL all'utente attivo). Se sposti la scheda su
|
||
un'altra porta USB il nome non segue: rilancia `setup-udev.sh`.
|
||
|
||
Il demone comunque ripiega su `/dev/ambilight` e poi `/dev/ttyUSB0` se il percorso
|
||
configurato non esiste, e riporta il dispositivo realmente aperto nel campo `dev`
|
||
di `ambilight-ctl state`.
|
||
|
||
## L'indicatore di condivisione schermo nel vassoio
|
||
|
||
Con la cattura via **portal ScreenCast**, KDE mostra nel vassoio un **pallino rosso**
|
||
finché la sessione di condivisione è attiva. Verificato su questa macchina:
|
||
|
||
- compare/scompare insieme allo streaming (diff di due screenshot: cambia solo quella
|
||
regione del vassoio, ~1.500 px in 172x22);
|
||
- **non** è un plasmoide di sistema (`/usr/share/plasma/plasmoids` non contiene nulla
|
||
di simile) e **non** compare in `knownItems`/`extraItems` del vassoio, quindi non
|
||
esiste la voce "Configure System Tray → Entries" per nasconderlo: è un indicatore
|
||
di stato del shell, per scelta di KDE.
|
||
|
||
**Il pallino è uno StatusNotifierItem pubblicato da `xdg-desktop-portal-kde`** (titolo
|
||
"Trasmissione dello schermo"): quindi è un elemento del vassoio con visibilità
|
||
configurabile da *Configura il vassoio di sistema → Voci* (impostandolo su "Sempre
|
||
nascosto" sparisce dal pannello e resta nel popup espanso).
|
||
|
||
Le alternative per non averlo:
|
||
|
||
1. **KWin ScreenShot2** (nessun portal, nessun indicatore) — **rifiutato su questa
|
||
macchina**: testato sia con `.desktop` utente sia con entry system-wide in
|
||
`/usr/local/share/applications` (`Exec=/usr/bin/python3`, `X-KDE-DBUS-Restricted-Interfaces=org.kde.KWin.ScreenShot2`,
|
||
`kbuildsycoca6`): la risposta resta `org.kde.KWin.ScreenShot2.Error.NoAuthorized`. KWin autorizza solo i
|
||
processi con un `.desktop` che dichiara `X-KDE-DBUS-Restricted-Interfaces=org.kde.KWin.ScreenShot2`.
|
||
Provato con un `.desktop` utente (`Exec=/usr/bin/python3` e `python3.14`) + `kbuildsycoca6`:
|
||
risponde ancora `org.kde.KWin.ScreenShot2.Error.NoAuthorized`. Non provato l'entry
|
||
in `/usr/local/share/applications` (richiede sudo). Se funziona, la cattura è un
|
||
`CaptureArea` della sola fascia (nessun indicatore, ma costo CPU per frame).
|
||
2. **kmsgrab** (`ffmpeg -f kmsgrab`) con `CAP_SYS_ADMIN`: **provato e non praticabile su
|
||
questa macchina** (Radeon 780M + KWin). Il piano primario è `plane-3` (id 206),
|
||
`crtc-0`, `fb=463`, `format=AR30` (`0x30335241`) con `modifier=0x200000010437b04`
|
||
(AMD DCC/tiled): ffmpeg risponde `Framebuffer pixel format 30335241 is not a known
|
||
supported format` e fallisce **prima** di applicare `-format`/`-format_modifier`
|
||
(provati `bgr0`, `rgb0`, `0rgb`, `bgra`, `x2rgb10le`, `-format_modifier 0`,
|
||
`-plane_id 206`). Anche superando il controllo, il layout tiled/DCC richiederebbe
|
||
un import GBM/EGL o VAAPI, che kmsgrab non fa.
|
||
3. Tenere il portal: costo GPU minimo, ~78 fps, indicatore visibile.
|
||
|
||
## Note
|
||
|
||
- Il firmware Adalight manda `Ada\n` una volta al secondo quando non riceve frame: è
|
||
l'heartbeat del protocollo, non un errore.
|
||
- Il demone tiene la seriale in esclusiva: Prismatik (o altri programmi Adalight) vanno chiusi.
|
||
- `ModemManager` su Fedora può contendere `/dev/ttyUSB0`: se la porta risulta occupata
|
||
serve una regola udev con `ID_MM_DEVICE_IGNORE`.
|
||
|
||
## Licenza
|
||
|
||
GPL-3.0-or-later (come i componenti KDE con cui interagisce).
|