From ecf59d1c7c74fe817ee4f9e392e319ad9a8b541a Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Wed, 21 Oct 2026 18:50:22 +0200 Subject: feat(accent): send the accent to the TV launcher udt-tv sends the window border's three hues to the tv-debloat launcher on the TV, which draws its focus highlight's edge as their gradient and uses the accent for its pill edges and title glow. One HMAC-SHA256-signed UDP datagram to port 47800; the address comes from ~/.config/udt/tv.json and the secret is the one tv-debloat deploys. Without either it does nothing. udt-accent starts it with the other helpers, install.sh links it. Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 9 +++++++ bin/udt-accent | 5 ++-- bin/udt-tv | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ install.sh | 2 +- 4 files changed, 96 insertions(+), 3 deletions(-) create mode 100755 bin/udt-tv diff --git a/AGENTS.md b/AGENTS.md index 5a31dee..a069dd7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -256,6 +256,15 @@ run reads the state first and skips an off bulb; and a request setting colour and brightness together, or a colour sent while a brightness fade runs, is partly dropped, so colour goes first and brightness after, separately. +`udt-tv` sends the same three hues to the TV launcher of the sibling +tv-debloat repo, which draws its focus highlight's edge as their gradient and +uses the accent for its pill edges and title glow. One HMAC-SHA256-signed UDP +datagram to the TV's port 47800 (the protocol is in tv-debloat's README), the +TV's address in `~/.config/udt/tv.json` and the secret shared with +tv-debloat's deploy in `~/.config/tv-debloat/accent.secret`; without either it +does nothing. There is no reply, so a TV in standby simply misses the change +and keeps the last colour it took. + **Sublime's, GRUB's and the homepage's accents are replaced, not substituted into a placeholder.** GRUB and the homepage are generated by udt-palette with the scheme's accent, then rewritten in place, before `udt-grub` and diff --git a/bin/udt-accent b/bin/udt-accent index 70557d8..1388db1 100755 --- a/bin/udt-accent +++ b/bin/udt-accent @@ -626,8 +626,9 @@ def main(image): # it only rebuilds and notifies, since the copy into /boot needs root. # The keyboard takes seconds per colour change, and is often not there. # The LED strip waits up to 20 s for its address broadcast, the bulbs - # up to 5 s per bulb for the gateway. All three read files written above. - for helper in ("udt-homepage", "udt-grub", "udt-keyboard", "udt-ledstrip", "udt-bulb"): + # up to 5 s per bulb for the gateway. All of them read files written above; + # udt-tv is a single UDP datagram to the TV launcher. + for helper in ("udt-homepage", "udt-grub", "udt-keyboard", "udt-ledstrip", "udt-bulb", "udt-tv"): subprocess.Popen([Path(__file__).resolve().parent / helper], stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, start_new_session=True) diff --git a/bin/udt-tv b/bin/udt-tv new file mode 100755 index 0000000..4406f7a --- /dev/null +++ b/bin/udt-tv @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +# udt-tv: send the wallpaper accent to the tv-debloat launcher on the TV. +# Copyright (C) 2026 Danilo M. +# Licensed under the GNU General Public License v2 only. +"""Tint the TV launcher's focus highlight, pills and title glow in the accent. + +Usage: udt-tv send the window border's three hues + udt-tv --selftest + +The colours are the three udt-accent wrote for the window border, the accent +between its two hue neighbours: the launcher draws its focus highlight's edge +as a gradient of all three and uses the middle one everywhere else. One UDP +datagram to the launcher's port 47800, "<#a,#b,#c> ", keyed with the secret the launcher was +deployed with. The launcher drops anything unsigned, more than a minute off +its clock, or not newer than the last one it took, and sends no reply, so +this cannot tell whether the TV took it (a TV in standby or a launcher that +Android killed misses it; the launcher keeps the last colour it got). + +The TV's address comes from ~/.config/udt/tv.json ({"host": ""}), the +secret from ~/.config/tv-debloat/accent.secret, the file tv-debloat's +deploy-launcher.sh pushes to the TV. Without either, this does nothing. +""" +import hashlib +import hmac +import json +import re +import socket +import sys +import time +from pathlib import Path + +CONFIG = Path.home() / ".config/udt/tv.json" +SECRET = Path.home() / ".config/tv-debloat/accent.secret" +BORDER = Path.home() / ".cache/udt/udt-border.lua" +PORT = 47800 + + +def colours(border): + """The border's rgb(rrggbb) hues as the launcher's "#a,#b,#c" field.""" + return ",".join("#" + h for h in re.findall(r"rgb\(([0-9a-fA-F]{6})\)", border)) + + +def message(field, secret, now): + body = f"{field} {now}" + return f"{body} {hmac.new(secret, body.encode(), hashlib.sha256).hexdigest()}" + + +def selftest(): + border = 'return { "rgb(fcd317)", "rgb(36d190)", "rgb(37daea)" }' + assert colours(border) == "#fcd317,#36d190,#37daea" + msg = message("#fcd317,#36d190,#37daea", b"k", 1700000000) + field, ts, mac = msg.split(" ") + assert (field, ts) == ("#fcd317,#36d190,#37daea", "1700000000") + assert mac == hmac.new(b"k", f"{field} {ts}".encode(), hashlib.sha256).hexdigest() + print("ok") + + +def main(): + args = sys.argv[1:] + if args == ["--selftest"]: + return selftest() + if args: + sys.exit(__doc__.split("\n\n")[1]) + try: + host = json.loads(CONFIG.read_text())["host"] + secret = SECRET.read_text().strip().encode() + field = colours(BORDER.read_text()) + except (OSError, ValueError, KeyError): + return + # The launcher takes one colour or three; anything else it would drop. + if field.count(",") not in (0, 2) or not field or not secret: + return + with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s: + try: + s.sendto(message(field, secret, int(time.time())).encode(), (host, PORT)) + except OSError as e: + print(f"udt-tv: {host}: {e}", file=sys.stderr) + + +if __name__ == "__main__": + main() diff --git a/install.sh b/install.sh index 13f634e..1c202d7 100755 --- a/install.sh +++ b/install.sh @@ -126,7 +126,7 @@ ln -sfn "$HOME/.cache/udt/colors.json" "$HOME/.cache/wal/colors.json" # The rofi menus' scripts live in the sibling rofi-scripts repo, which links # its own. -for f in udt-accent udt-grub udt-keyboard udt-ledstrip udt-bulb udt-steps udt-palette udt-machine-name; do +for f in udt-accent udt-grub udt-keyboard udt-ledstrip udt-bulb udt-tv udt-steps udt-palette udt-machine-name; do ln -sfn "$repo/bin/$f" "$HOME/bin/$f" done -- cgit v1.2.3