Feat: Peripheral status and integration #140

Merged
zach merged 17 commits from feat/peripheral-integration into main 2026-08-16 18:03:42 +02:00
Owner

What & why

WIP. Aims to add support for storing and displaying information about connected wireless peripherals. Will most likely be targeted mostly toward displaying battery percentages.

  • Support for general devices through UPower path
  • Support for specific devices through vendor's specific API paths
    • Logitech
    • Others (will be added later)

Battery polling daemon is started through QML if the UPower widget is enabled. The daemon is slow to initialize, but once warmed up it will report device stats with a 15 second interval. It uses a lock file to prevent multiple daemons from spawning.

The battery widget on the bar shows the device with the currently lowest battery percentage.

Related issue

#139

Type

New feature

Tested

  • Build succeeds
  • Existing tests pass
  • Manually verified

Breaking changes?

None

Checklist

  • CI passes (format, lint, typecheck)
  • Clean commits
  • No new warnings
  • Docs updated if needed
### What & why WIP. Aims to add support for storing and displaying information about connected wireless peripherals. Will most likely be targeted mostly toward displaying battery percentages. - [x] Support for general devices through UPower path - [ ] Support for specific devices through vendor's specific API paths - [x] Logitech - [ ] Others (will be added later) Battery polling daemon is started through QML if the UPower widget is enabled. The daemon is slow to initialize, but once warmed up it will report device stats with a 15 second interval. It uses a lock file to prevent multiple daemons from spawning. The battery widget on the bar shows the device with the currently lowest battery percentage. ### Related issue #139 ### Type New feature ### Tested - [x] Build succeeds - [x] Existing tests pass - [x] Manually verified ### Breaking changes? None ### Checklist - [x] CI passes (format, lint, typecheck) - [x] Clean commits - [x] No new warnings - [x] Docs updated if needed
zach added 1 commit 2026-07-12 17:59:45 +02:00
initial addition of battery display
JS/TS / fmt (pull_request) Successful in 7s
JS/TS / lint (pull_request) Successful in 9s
Python / fmt (pull_request) Successful in 15s
Python / lint (pull_request) Failing after 19s
Python / typecheck (pull_request) Failing after 1m5s
C++ / fmt (pull_request) Successful in 6s
Python / test (pull_request) Successful in 1m20s
Rust / fmt (pull_request) Successful in 1m42s
Python / buildcheck (pull_request) Successful in 2m23s
Rust / clippy (pull_request) Successful in 2m42s
Rust / build (pull_request) Successful in 2m53s
C++ / build (pull_request) Successful in 2m22s
C++ / clang-tidy (pull_request) Successful in 3m17s
8789d2d322
zach added 1 commit 2026-07-13 00:48:17 +02:00
add support for all UPower peripherals
C++ / fmt (pull_request) Successful in 3s
JS/TS / fmt (pull_request) Successful in 17s
JS/TS / lint (pull_request) Successful in 16s
Python / fmt (pull_request) Successful in 34s
Python / lint (pull_request) Successful in 35s
Python / typecheck (pull_request) Failing after 1m5s
C++ / build (pull_request) Successful in 1m36s
Python / test (pull_request) Successful in 1m26s
Rust / fmt (pull_request) Successful in 48s
Rust / build (pull_request) Successful in 1m41s
Rust / clippy (pull_request) Successful in 1m37s
Python / buildcheck (pull_request) Successful in 2m41s
C++ / clang-tidy (pull_request) Successful in 3m32s
4ee9bb3f12
zach added 1 commit 2026-07-13 01:13:01 +02:00
Fix cmake nuitka build command
C++ / fmt (pull_request) Successful in 5s
JS/TS / fmt (pull_request) Successful in 12s
JS/TS / lint (pull_request) Successful in 20s
Python / fmt (pull_request) Successful in 31s
Python / lint (pull_request) Successful in 33s
Python / test (pull_request) Successful in 1m0s
Python / typecheck (pull_request) Failing after 1m5s
C++ / build (pull_request) Successful in 1m39s
Rust / fmt (pull_request) Successful in 50s
Rust / build (pull_request) Successful in 1m41s
Python / buildcheck (pull_request) Successful in 2m35s
Rust / clippy (pull_request) Successful in 2m5s
C++ / clang-tidy (pull_request) Successful in 6m13s
d6230bef34
zach added 1 commit 2026-07-13 01:14:00 +02:00
gitignore
C++ / fmt (pull_request) Successful in 8s
JS/TS / fmt (pull_request) Successful in 12s
JS/TS / lint (pull_request) Successful in 15s
Python / fmt (pull_request) Successful in 26s
Python / lint (pull_request) Successful in 31s
Python / test (pull_request) Successful in 54s
Python / typecheck (pull_request) Failing after 50s
C++ / build (pull_request) Successful in 3m19s
Rust / fmt (pull_request) Successful in 41s
Rust / build (pull_request) Successful in 1m43s
Python / buildcheck (pull_request) Successful in 2m35s
Rust / clippy (pull_request) Successful in 1m27s
C++ / clang-tidy (pull_request) Successful in 4m52s
259a4dde7a
zach added 1 commit 2026-07-13 01:34:00 +02:00
prevent writing battery json when there has been no change, filter out devices with unknown state
C++ / fmt (pull_request) Successful in 4s
JS/TS / fmt (pull_request) Successful in 11s
JS/TS / lint (pull_request) Successful in 15s
Python / fmt (pull_request) Successful in 29s
Python / lint (pull_request) Successful in 32s
Python / test (pull_request) Successful in 1m4s
Python / typecheck (pull_request) Failing after 1m10s
C++ / build (pull_request) Successful in 1m35s
Rust / fmt (pull_request) Successful in 36s
Rust / build (pull_request) Successful in 1m34s
Rust / clippy (pull_request) Successful in 1m28s
Python / buildcheck (pull_request) Successful in 2m35s
C++ / clang-tidy (pull_request) Successful in 3m32s
85320e9ea1
zach added 1 commit 2026-07-13 14:57:02 +02:00
backend now more robust, threaded writes use locks
C++ / fmt (pull_request) Successful in 4s
JS/TS / fmt (pull_request) Successful in 11s
JS/TS / lint (pull_request) Successful in 21s
Python / lint (pull_request) Failing after 31s
Python / fmt (pull_request) Successful in 38s
Python / test (pull_request) Failing after 1m0s
Python / typecheck (pull_request) Failing after 1m1s
C++ / build (pull_request) Successful in 2m0s
Rust / fmt (pull_request) Successful in 44s
Rust / build (pull_request) Successful in 1m40s
Rust / clippy (pull_request) Successful in 1m34s
Python / buildcheck (pull_request) Successful in 2m35s
C++ / clang-tidy (pull_request) Successful in 3m44s
620fe001a4
zach added 1 commit 2026-07-13 15:01:07 +02:00
fix: cmake nuitka command
C++ / fmt (pull_request) Successful in 4s
JS/TS / fmt (pull_request) Successful in 15s
JS/TS / lint (pull_request) Successful in 16s
Python / lint (pull_request) Failing after 32s
Python / fmt (pull_request) Successful in 36s
Python / test (pull_request) Failing after 1m3s
C++ / build (pull_request) Successful in 1m39s
Python / typecheck (pull_request) Failing after 1m9s
Rust / fmt (pull_request) Successful in 38s
Rust / build (pull_request) Successful in 1m33s
Python / buildcheck (pull_request) Successful in 2m48s
Rust / clippy (pull_request) Successful in 2m1s
C++ / clang-tidy (pull_request) Successful in 5m51s
57147dc7c1
zach added 1 commit 2026-07-13 15:03:20 +02:00
fix: cmake nuitka command again
C++ / fmt (pull_request) Successful in 9s
JS/TS / fmt (pull_request) Successful in 13s
JS/TS / lint (pull_request) Successful in 17s
Python / fmt (pull_request) Successful in 43s
Python / lint (pull_request) Failing after 32s
Python / test (pull_request) Failing after 57s
Python / typecheck (pull_request) Failing after 1m0s
C++ / build (pull_request) Successful in 3m5s
Rust / fmt (pull_request) Successful in 41s
Rust / build (pull_request) Successful in 1m39s
Python / buildcheck (pull_request) Successful in 2m36s
Rust / clippy (pull_request) Successful in 1m21s
C++ / clang-tidy (pull_request) Successful in 4m49s
bb50cf756d
Collaborator

I have tested the following devices:

  • Two logitech mice (G pro x and mx vertical)
  • Dualsense 5 controller
  • Sennheiser momentum 4 ANC headphones
  • A sony soundbar (testing if this does not appear)

The mice show up. Controller shows up. ANC headphone does not report on the module, but is visible in blueman. Soundbar correctly does not show up since it has no battery.

I will do a thorough review once the PR is out of WIP.

I have tested the following devices: - Two logitech mice (G pro x and mx vertical) - Dualsense 5 controller - Sennheiser momentum 4 ANC headphones - A sony soundbar (testing if this does not appear) The mice show up. Controller shows up. ANC headphone does not report on the module, but is visible in blueman. Soundbar correctly does not show up since it has no battery. I will do a thorough review once the PR is out of WIP.
zach added 1 commit 2026-07-13 19:31:52 +02:00
improve device detection when plugging/unplugging
C++ / fmt (pull_request) Successful in 4s
JS/TS / fmt (pull_request) Successful in 19s
JS/TS / lint (pull_request) Successful in 19s
Python / fmt (pull_request) Successful in 36s
Python / lint (pull_request) Failing after 34s
Python / test (pull_request) Failing after 1m2s
Python / typecheck (pull_request) Failing after 1m11s
C++ / build (pull_request) Successful in 2m11s
Rust / fmt (pull_request) Successful in 52s
Rust / build (pull_request) Successful in 1m45s
Rust / clippy (pull_request) Successful in 1m35s
Python / buildcheck (pull_request) Successful in 2m44s
C++ / clang-tidy (pull_request) Successful in 3m37s
a9bbb21f54
zach added 1 commit 2026-07-13 22:10:47 +02:00
simplify dedupe
C++ / fmt (pull_request) Successful in 3s
JS/TS / fmt (pull_request) Successful in 16s
JS/TS / lint (pull_request) Successful in 19s
Python / fmt (pull_request) Successful in 34s
Python / lint (pull_request) Failing after 31s
Python / test (pull_request) Failing after 59s
Python / typecheck (pull_request) Failing after 1m7s
C++ / build (pull_request) Successful in 1m54s
Rust / fmt (pull_request) Successful in 49s
Rust / build (pull_request) Successful in 1m41s
Rust / clippy (pull_request) Successful in 1m33s
Python / buildcheck (pull_request) Successful in 2m38s
C++ / clang-tidy (pull_request) Successful in 3m31s
bf5a8b8049
zach added 1 commit 2026-08-15 20:55:05 +02:00
Merge branch 'main' into feat/peripheral-integration
C++ / fmt (pull_request) Successful in 4s
JS/TS / fmt (pull_request) Successful in 20s
JS/TS / lint (pull_request) Successful in 18s
Python / lint (pull_request) Failing after 30s
Python / fmt (pull_request) Successful in 35s
Python / test (pull_request) Failing after 1m1s
Python / typecheck (pull_request) Failing after 1m24s
Rust / fmt (pull_request) Successful in 33s
Rust / build (pull_request) Successful in 1m51s
C++ / build (pull_request) Successful in 2m42s
Rust / clippy (pull_request) Successful in 1m1s
Python / buildcheck (pull_request) Successful in 2m34s
C++ / clang-tidy (pull_request) Successful in 4m2s
070fe7add0
zach changed title from WIP: Peripheral status and integration to Feat: Peripheral status and integration 2026-08-15 20:55:29 +02:00
AramJonghu force-pushed feat/peripheral-integration from 094fc568f1 to 5a01c4b472 2026-08-15 22:02:03 +02:00 Compare
Collaborator

Added commits that deal with the failing CI/CD.

I discovered containers in docker and workflows to use a slim container. This is good to only get a minimum required container to do the job. Here is a snippit of the change:

jobs:
    static:
        runs-on: debian
        container: node:26-trixie-slim

I also changed from alpine to debian since additional dependencies are required to conform to Alpine's use of musl.

Amount of jobs is reduced as well for python's CI. Will likely do it for all CI checks later (including using slim when beneficial), just python for now. Reason being to reduce reinstalling packages or rebuilding for each separate job. Python's CI now is separated from static and a verify check. Verify is what will be required as that runs the typecheck, tests, and nuitka build check.

Currently, typecheck is on the verify job. This could go to static if you do not deem this an important check. let me know about that.

Pyproject was also missing dependencies for the CI to grab all needed dependencies. Initially, had optional dependencies, but this is likely unnecessary as we have few dependencies as of now.

Added commits that deal with the failing CI/CD. I discovered containers in docker and workflows to use a slim container. This is good to only get a minimum required container to do the job. Here is a snippit of the change: ```yml jobs: static: runs-on: debian container: node:26-trixie-slim ``` I also changed from alpine to debian since additional dependencies are required to conform to Alpine's use of musl. Amount of jobs is reduced as well for python's CI. Will likely do it for all CI checks later (including using slim when beneficial), just python for now. Reason being to reduce reinstalling packages or rebuilding for each separate job. Python's CI now is separated from static and a verify check. Verify is what will be required as that runs the typecheck, tests, and nuitka build check. Currently, typecheck is on the verify job. This could go to static if you do not deem this an important check. let me know about that. Pyproject was also missing dependencies for the CI to grab all needed dependencies. Initially, had optional dependencies, but this is likely unnecessary as we have few dependencies as of now.
AramJonghu added 2 commits 2026-08-15 22:07:58 +02:00
chore: added missing dependency to pyproject
C++ / fmt (pull_request) Successful in 4s
JS/TS / fmt (pull_request) Successful in 17s
JS/TS / lint (pull_request) Successful in 21s
Python / static (pull_request) Failing after 1m24s
Rust / fmt (pull_request) Successful in 1m16s
C++ / build (pull_request) Successful in 2m26s
Rust / build (pull_request) Successful in 2m7s
Rust / clippy (pull_request) Successful in 1m4s
Python / verify (pull_request) Failing after 3m16s
C++ / clang-tidy (pull_request) Successful in 3m53s
8883d98b00
AramJonghu force-pushed feat/peripheral-integration from 5a01c4b472 to 8883d98b00 2026-08-15 22:07:58 +02:00 Compare
AramJonghu requested review from AramJonghu 2026-08-15 22:20:44 +02:00
zach added 1 commit 2026-08-16 11:11:42 +02:00
Merge branch 'main' into feat/peripheral-integration
C++ / fmt (pull_request) Successful in 4s
JS/TS / fmt (pull_request) Successful in 18s
JS/TS / lint (pull_request) Successful in 20s
Python / static (pull_request) Failing after 59s
Rust / fmt (pull_request) Successful in 1m4s
Rust / build (pull_request) Successful in 1m56s
C++ / build (pull_request) Successful in 2m31s
Python / verify (pull_request) Failing after 2m26s
Rust / clippy (pull_request) Successful in 1m22s
C++ / clang-tidy (pull_request) Successful in 3m53s
da21675f03
zach added 1 commit 2026-08-16 11:24:02 +02:00
fix unused vars
C++ / fmt (pull_request) Successful in 7s
Python / static (pull_request) Failing after 2s
JS/TS / fmt (pull_request) Successful in 25s
JS/TS / lint (pull_request) Successful in 21s
Rust / fmt (pull_request) Successful in 55s
Rust / build (pull_request) Successful in 2m22s
Python / verify (pull_request) Failing after 2m41s
Rust / clippy (pull_request) Successful in 1m45s
C++ / build (pull_request) Successful in 3m38s
C++ / clang-tidy (pull_request) Successful in 4m46s
ba567efc22
zach added 1 commit 2026-08-16 12:51:09 +02:00
Merge branch 'main' into feat/peripheral-integration
C++ / fmt (pull_request) Successful in 7s
JS/TS / fmt (pull_request) Successful in 15s
JS/TS / lint (pull_request) Successful in 15s
Python / static (pull_request) Successful in 59s
Rust / fmt (pull_request) Successful in 50s
Python / verify (pull_request) Failing after 2m37s
Rust / build (pull_request) Successful in 2m1s
C++ / build (pull_request) Successful in 4m7s
Rust / clippy (pull_request) Successful in 1m36s
C++ / clang-tidy (pull_request) Successful in 5m17s
bfaba488f3
zach added 1 commit 2026-08-16 13:11:22 +02:00
fix python errors
C++ / fmt (pull_request) Successful in 10s
JS/TS / fmt (pull_request) Successful in 15s
JS/TS / lint (pull_request) Successful in 15s
Python / static (pull_request) Successful in 53s
Rust / fmt (pull_request) Successful in 1m6s
Rust / build (pull_request) Successful in 2m10s
C++ / build (pull_request) Successful in 2m37s
Rust / clippy (pull_request) Successful in 1m33s
Python / verify (pull_request) Successful in 2m53s
C++ / clang-tidy (pull_request) Successful in 3m59s
33c9432390
AramJonghu approved these changes 2026-08-16 17:20:09 +02:00
AramJonghu left a comment
Collaborator

battery.py could be separated into modules. Multiple sub-features into separate files could improve readability, though that could be my preference. Reason is that battery.py does not give much of a hint what the file is as a sub command. Separating the file into multiple modules separates separate functions such as: peripheral/config.py, peripheral/listener.py, and peripheral/daemon.py.

Now, I have not read every line in detail to ascertain proper separation, so module naming and the amount of separation are things I am unsure of.

Other than that, code looks good to me. I will approve and let you decide if separating into modules is what you want to do.

`battery.py` could be separated into modules. Multiple sub-features into separate files could improve readability, though that could be my preference. Reason is that `battery.py` does not give much of a hint what the file is as a sub command. Separating the file into multiple modules separates separate functions such as: `peripheral/config.py`, `peripheral/listener.py`, and `peripheral/daemon.py`. Now, I have not read every line in detail to ascertain proper separation, so module naming and the amount of separation are things I am unsure of. Other than that, code looks good to me. I will approve and let you decide if separating into modules is what you want to do.
@@ -0,0 +244,4 @@
def _start_hotplug_watcher(on_hotplug: Callable[[], None]):
try:
import pyudev
Collaborator

imports are done within try catch statements. Is this commonplace in Python rather then importing into the file at the top?

`import`s are done within try catch statements. Is this commonplace in Python rather then importing into the file at the top?
Author
Owner

Has to do with that a global import would create naming conflicts.

Has to do with that a global import would create naming conflicts.
AramJonghu marked this conversation as resolved
Author
Owner

battery.py could be separated into modules. Multiple sub-features into separate files could improve readability, though that could be my preference. Reason is that battery.py does not give much of a hint what the file is as a sub command. Separating the file into multiple modules separates separate functions such as: peripheral/config.py, peripheral/listener.py, and peripheral/daemon.py.

Now, I have not read every line in detail to ascertain proper separation, so module naming and the amount of separation are things I am unsure of.

Other than that, code looks good to me. I will approve and let you decide if separating into modules is what you want to do.

Could be a good idea, yeah. Though it's pretty low on the priority list for me honestly, since the reason for splitting the file wouldn't be to make components reusable and strictly only to increase readability.

I will merge this, then.

> `battery.py` could be separated into modules. Multiple sub-features into separate files could improve readability, though that could be my preference. Reason is that `battery.py` does not give much of a hint what the file is as a sub command. Separating the file into multiple modules separates separate functions such as: `peripheral/config.py`, `peripheral/listener.py`, and `peripheral/daemon.py`. > > Now, I have not read every line in detail to ascertain proper separation, so module naming and the amount of separation are things I am unsure of. > > Other than that, code looks good to me. I will approve and let you decide if separating into modules is what you want to do. Could be a good idea, yeah. Though it's pretty low on the priority list for me honestly, since the reason for splitting the file wouldn't be to make components reusable and strictly only to increase readability. I will merge this, then.
zach merged commit 03ef51cd0c into main 2026-08-16 18:03:42 +02:00
zach deleted branch feat/peripheral-integration 2026-08-16 18:03:43 +02:00
Sign in to join this conversation.