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

./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.

Uso da terminale

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):

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).

S
Description
Ambilight per KDE Plasma 6: demone Adalight (ESP8266/WS2812B) + applet nel vassoio con slider luminosita, contrasto, saturazione
Readme
113 KiB
Languages
Python 62.7%
QML 22.1%
Shell 15.2%