Skip to content

alex-mazzariol/qubes-lcd-dashboard

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Qubes LCD Dashboard

A refactored, modular skeleton for your Qubes OS + USB LCD dashboard.

Due to the very nature of Qubes OS, some design choices might not reflect the optimal focus of each individual workstation build: this is why this repository is not to be intended as a finished product. I encourage each interested person in budgeting the effort to customize, if slightly, this software to each individual use case.

Bits and pieces, and licenses

The LCD communication library (vendored in src/library) is a GNU General Public License version 3 component straight out of https://github.com/mathoudebine/turing-smart-screen-python by Matthieu Houdebine.

The Roboto and Roboto Mono fonts (vendored in src/lcd_qubes/assets) are distributed by Google Fonts under the Apache 2.0 license.

License text for each component is available inside the vendoring directory.

Sample result

A picture of the LCD display with the dashboard

Layout

  • config.py — CLI
  • state.py — typed shared state
  • runner.py — safe subprocess helpers
  • parsers.py — pure functions that parse command outputs
  • quarter_blinker.py — the tool to blink the backlight (or led color)
  • monitors.py — lightweight threads that collect metrics into state
  • dashboard.py — widgets and draw orchestration
  • app.py — wiring, signals, lifecycle

Notes

  • Time-based loops use time.monotonic() and threading.Event.wait() for stable cadence.
  • All subprocess calls are bounded with timeouts and never raise on non-zero return codes.
  • Drawing is incremental: widgets redraw only when their value changes.

Quarter-hour backlight blink

Get a subtle heads-up before the next quarter-hour (handy reminder for work meetings, that are usually aligned to the quarter hour). It is enabled by default, with brightness cycling of 1Hz for 20 seconds. Tune parameters in config.py or by command line.

Installation

The following instructions are valid and tested on Qubes R4.2, and should most likely work on R4.3.

Top-level steps:

  • copy dom0-service.py in dom0 and have it reachable by the Qube that will display the dashboard
  • copy the dashboard in the Qube that will connect to the display and set it as a systemd unit
  • customize everything as needed

Dom0

Copy dom0-service.py into the appropriate directory. Also make sure to check all guidelines in https://doc.qubes-os.org/en/latest/user/how-to-guides/how-to-copy-from-dom0.html#copying-to-dom0 and understand the security implications. I promise this software does not impair the security of your dom0, but don't trust me.

Inside dom0, given that a-personal is your machine with this repo on it, and the repo is in /home/user/projects/qubes-lcd-dashboard:

$ sudo qvm-run --pass-io a-personal 'cat /home/user/projects/qubes-lcd-dashboard/dom0-service.py' > /etc/qubes-rpc/dashboard.GetValues
$ sudo chmod 0755 /etc/qubes-rpc/dashboard.GetValues

Then test it to see if the values are returned - in dom0:

$ /etc/qubes-rpc/dashboard.GetValues
{"timestamp":1759143260.8661358,"sensors":{"cpu_temp":53.875,"gpu_temp":36.0},"ups":{"load_pct":28.0,"runtime_min":16.5},"qubes":{"running":9,"needs_update":[]}}

Your mileage might obviously vary and you'll most likely have to change mapping for sensors and UPS name, if you even have an UPS at all. Yes I did install NUT on dom0, and I keep the UPS connected to dom0. The above-linked security reasoning points still apply, and you can easily have the UPS connected to another Qube, where you can either have NUT and another QRExec service, or have NUT be on the same Qube as the dashboard, either case some adjustment is required on the source code.

Last, set the qrexec policy to allow service access to the Qube that will actually display the data to the LCD screen. In my case it's a-personal, so, in dom0, create the file /etc/qubes/policy.d/60-dashboard.policy with this content:

dashboard.GetValues *   a-personal   dom0    allow
dashboard.GetValues *   @anyvm      dom0    deny

It will be immediately effective without restarting anything. Try calling it from the allowed Qube - in my case, in a-personal:

[user@a-personal ~]$ qrexec-client-vm dom0 dashboard.GetValues
{"timestamp":1759143598.3346984,"sensors":{"cpu_temp":54.25,"gpu_temp":36.0},"ups":{"load_pct":26.0,"runtime_min":16.0},"qubes":{"running":9,"needs_update":[]}}

Display Qube

In my case the display Qube is a-personal, so I'll be setting up the display service there. It will call dom0 to ask for updates in a secure fashion, and will concern itself with displaying bitmaps and whatnot over USB connection.

Ensure the USB display is passed from any USB qube to the display qube, e.g.:

[user@dom0 ~]$ qvm-usb
BACKEND:DEVID     DESCRIPTION
[...]
sys-usbvm:2-3.1   1a86:5722_Turing_UsbMonitor_xxx

[user@dom0 ~]$ qvm-usb a --persistent a-personal sys-usbvm:2-3.1

Then copy the repository in the Qube and run install-online.sh. The script creates a virtual environment in the current user and tries to set up the service as a user systemd unit. The service is started immediately; however it might not work just out of the box.

The user the service is started as should have permission to access the USB-to-serial serial port (e.g. /dev/ttyACM0 or such). Permission might depend on the template VM used for that Qube. In my case, using Fedora 42, I had to run

[user@fc42 ~]$ sudo usermod -aG dialout user

in the template to have the user user join the dialout group and thus be able to access serial ports, and have this modification persist across Qube reboots. The usual security paranoia applies here too: if a permission is not needed it should not be granted, and if you feel like, you can use a separate template for this purpose or have a startup script for the service give permissions to /dev/ttyACMxxx as appropriate using the passwordless sudo feature.

Caveats

The app.py code is hardwired to a Turing LCD display rev A board, in portrait mode, with a resolution of 320 x 480, which is my current display. If you need a different display you'll most likely have to change the widget placement on the dashboard.

The current src/library directory is a straight copy from Matthieu Houdebine's project, as cited above, and the code inside it expects to be able to write a log file which I could not turn off unless I modify the src/library/log.py file. Since I expect to update the library in the future, and I would keep it a simple copy&paste operation from the upstream repository, I would not modify any files inside the src/library directory. The log file is managed by the SystemD Unit, that mounts a temporary current directory where the log file can be written and will not be persisted.

About

A simple python dashboard for USB LCD displays for Qubes OS

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published