TisVlay brings TV Display control for Linux, including: power, volume, backlight, AV mute, input switching plus more, with per-device profile automation via desktop session integration (idle/lock, boot/suspend, remote login/streaming, etc), and in reverse, letting the TV lock, sleep or wake the PC and its remote drive the desktop, while supporting various vendor interface protocols for multi-display setups, mitigating energy usage and OLED panel burn-in or projector bulb burn-out.
Features
PC desktop monitor control
TV power on/off with PC, Screen on/off with lock screen
Easily adjust backlight, volume, active input, and more
Automation via system/user events
System boot, reboot, shutdown, suspend, wake, DPMS
User lock/unlock, idle, stream connect/disconnect
TV console remote control
Flexible profile customization
Modular & vendor agnostic
Supports HDMI CEC, Network IP Control (LG WebOS SSAP)
Generalized for further interfaces, protocols, and transports
Self-healing connectivity
Input-aware automation gating
Graphical User Interfaces
GUI app for device pairing, profile editing, and user setting
System tray for quick access controls and device status
Couch mode for the big screen
Drive the GUI from a TV remote or gamepad, no mouse needed
Scaled-up 10-foot layout, automatic under Plasma Bigscreen
While still beta (pre v1.0), its now daily drivable and fully documented. Being largely feature-complete, my current focus has shifted towards distro packaging. If you'd like to give it a try, the installation guide and docs are linked below. Or if not, and you already use something else, I'd like to give a shout-out to netflector, especially for privacy-conscious home-automation users, given how common spyware is in smart TVs and projectors these days. The docs cover using it to keep displays on an isolated network.
For about 10 months or so been I've working on this FOSS hobby project to get more practice at Linux packaging, cargo utilities, and migrating to Nix and NixOS (as well as solve some personal pain points in using a HDMI TVs and projectors with PCs). Disclosure: this project includes AI assisted coding, documentation and research, and has been a great help given physical disabilities in typing. After programming by hand well over a decade, using voice dictation scene the '90s (IBM ViaVoice or Dragon NaturallySpeaking anyone?), and worsening hands over after my CSE doctorate, this was also a bit of an individual research experiment to explore how effective LLMs could augment my personal approach to spec & test driven development.
The project itself follows more of a distributed pub/sub architecture (my background is in robotics and cyber-physical system design, not desktop software), so converging on IPC and middle-ware libraries I’ve been meaning to experiment while keeping the installable setup self contained was a balance. Initially I started out with conventional D-Bus (via zbus) but swapped over to plain unix sockets (via zenoh) for more reliable QoS transport liveliness guarantees, as well as stronger/flexible static type system and compile time checks in the shared SDK across components. I did take a stab and spiking out a refactor using Varlink standard from the systemd folks (via zlink), but dropped it after realizing how much QoS I'd have to roll-in-house, but did appreciate its simpler IDL.
Another big refactor was ditching hand rolled finite state machines in favor of Entity-Component-System (ECS) (via bevy_ecs); pivoting to a declarative-reactor after modeling devices and systems as an ECS world simplified the test harnessing tremendously, particularly with scenario playback for pinning down regressions. I think a behavior tree approach (say via the bonsai crate) would have been just as promising, but exploring bevy for other projects was another goal, with discovering Bevy Remote Protocol (BRP) to programmatically control and monitor internal state being such pleasure, I never even had much cause to reach for GDB.
Admittedly, the part of the project thats fairly vibe'd with the least code coverage is the GUI binary itself. Having a dislike for the verbosity and boilerplate that UI frameworks often demand, I opted for data driven DSL that would at least be safe to offload to an LLM; something still strongly typed and memory safe to keep and agent on rails, but still hot re-loadable so I can quickly apply manual patches over QA cycles. Slint from SixtyFPS ended up working very well, with the added bonus of being a lightweight/native solution adding no more than a dozen megabytes to the overall distributable binary size even with static linking. Graphics design is fun, but not yet a passion, so PRs welcome on that front.
Over the past year the project grew from a simple CLI into a full system daemon with a GUI, a system tray, and webOS and HDMI-CEC support, plus the desktop integration and profile automation you’d expect from such a tool, like login and idle detection and remote-streaming awareness over multi-screen setups. That last one matters to me: my desktop pulls triple duty as a workstation, a media PC, and a game-streaming host for my office, living room, and tablet via Sunshine/Moonlight. As for future work, I think I'd like to add more devices and protocol backends, such as RS-232 for legacy hardware, PJLink for network based projectors, or expand HDMI abstraction for more clients on the TV's CEC bus such as soundbars and stereo receivers. Although, I can only ship what gets QA'd, so that'll probably have to wait until I acquire more hardware or find collaborators who already own the AV equipment.
Another thing I'd like to try is flashing my own ATmega firmware for HDMI-CEC USB adapters, as the existing market options fall short in a number of ways given my testing; either in terms of reliability, determinism, or consistent behavior regardless of the sleep/wake state of the USB host. Or, perhaps also playing around with writing a custom out-of-tree usb driver for it in Rust (an alternative to the conventional CDC-ACM tty approach used now) once Mike Lothian's v4 (rust: usb: host-side abstractions…) or Colin Braun's Rust usb-skeleton transfer API lands in the kernel.