Omahub
← All plugins
D

Device Battery Stats

by Deoxizn

Battery and charging status for Bluetooth and 2.4GHz wireless devices.

Security review

No obvious issues detected

Deterministic scan — not a security guarantee

None
Risk level
None
Analyzed commit
b8f702d
Scanned
1 month ago

No potentially dangerous behavior detected in the analyzed commit.

Automated analysis only — not a security guarantee.

AI advisory review

No obvious issues detected

Language-model assessment · ~deepseek/deepseek-v4-flash-latest — advisory only

Low
AI risk level
Low
Recommendation
install
Model
~deepseek/deepseek-v4-flash-latest
Analyzed commit
b8f702d
Reviewed
1 month ago

The plugin is a read-only battery/charging status collector for wireless peripherals. It reads system information (sysfs, BlueZ via busctl, upower, bluetoothctl) and runs a Python collector, but performs no writes, no network calls, and no destructive operations. The only minor concern is that the bash fallback builds JSON with minimal escaping, which could produce malformed output for unusual device names, but this is a display bug, not a security risk.

  • The bash fallback in scripts/device-battery.sh constructs JSON with only basic escaping; unusual device names could produce malformed JSON, but this only affects the widget display and is not a security issue.
  • The plugin reads system battery information and runs subprocesses (busctl, upower, bluetoothctl, python3), but all are read-only queries with timeouts and no user data is exfiltrated.
How this check works

This review combines the deterministic scan (the rule-based results above) with an independent look at the plugin's code by a language model. The model reads a trimmed sample of the repository's files, the manifest, and the README, then gives a plain-language risk level and a recommendation: install (no notable danger), review (look closer first), or avoid (clearly dangerous).

It runs on the same analyzed commit as the deterministic scan and is strictly advisory — it is not a security guarantee and never blocks a plugin by itself. A human moderator still reviews plugins before they are listed.

AI advisory only — automated analysis, not a security guarantee.

Install
$ omarchy plugin add https://github.com/Deoxizn/devicebattstats --enable
System #bar

Device Battery Stats

An Omarchy (Quattro) plugin that shows battery level and charging state for wireless peripherals directly on the bar, with a details popup. Works natively on both the stock Omarchy bar and the Shibumi bar.

The point of the plugin is closing a gap Omarchy does not cover: 2.4GHz receivers (Logitech hidpp dongles, Keychron 2.4GHz mode, ...) that expose a battery through the kernel's power_supply class but never show up in BlueZ. Bluetooth keyboards and mice that expose a battery via BlueZ Battery1 are covered too, as are Razer wireless peripherals through python-openrazer.

<p align="center"> <img src="preview.png" alt="Device Battery Stats: bar pill and device details popup" /> </p>

Features

  • Bar pill showing one glyph per connected device (keyboard, mouse, ...); the classic "battery glyph + lowest percent" is available via the displayMode setting
  • Glyph fills and tints while any device is charging or low
  • Hover tooltip listing every device
  • Click popup with one row per device: type glyph, name, source, charge state and a mini battery bar
  • Text and icons automatically sized and colored to match the host bar
  • Polls only while a widget is mounted (no background churn)
  • Graceful degradation: works with whatever subset of tools is installed

Data sources

scripts/device-battery.sh emits one JSON document per run, aggregating with graceful degradation per source:

Source How it is read
Bluetooth busctl object-manager dump of BlueZ, org.bluez.Battery1
sysfs /sys/class/power_supply/* (Logitech hidpp, 2.4GHz dongles)
upower upower -e / upower -i, deduplicated against BlueZ
Razer python-openrazer DeviceManager (session bus org.razer)

Environment variables:

  • DEVICEBATT_TIMEOUT — per-subprocess timeout in seconds (default 5).
  • DEVICEBATT_INCLUDE_SYSTEM=1 — include laptop System-scope batteries.

Requirements

  • Quickshell (the shell Omarchy ships)
  • Material Symbols Rounded font (installed with Omarchy)
  • busctl, upower, bluetoothctl (usual on Omarchy)
  • optional: python-openrazer for Razer devices

Install

omarchy plugin add https://github.com/Deoxizn/devicebattstats.git --enable

omarchy plugin add clones the repo, validates it, and enables the widget in the right section of the bar.

Updating

omarchy plugin update dev.deoxizn.devicebattstats

omarchy plugin update fetches the latest version, shows the diff, and asks before applying it. Run it without an id to update every installed git-managed plugin.

Uninstall

omarchy plugin remove dev.deoxizn.devicebattstats

Widget settings

Inline layout-entry options:

  • displayMode: what the bar pill shows:
    • devices (default) — one glyph per connected device (keyboard, mouse, headset, ...), hidden when nothing is connected. A glyph is filled with the accent color when that device is low or charging.
    • full — a battery glyph plus the lowest known charge percent
    • icon — battery glyph only
    • text — lowest charge percent only
  • compact: true forces icon mode (kept for compatibility with the compact option Omarchy widgets use)

Theming

The widget never hard-codes colors or sizes. It reads everything from the host bar through a small token adapter:

  • Shibumi bar — the bar exposes its own VisualTokens (bar.visualTokens: labelSize, iconSize, ink, seal, paper, ...), so per-widget color fills configured in Shibumi settings apply to this widget too.
  • Stock Omarchy bar / any Quattro host — qml/HostTokens.qml derives the same interface from the bar's standard properties (fontFamily, foreground, urgent, background, barSize, vertical) and falls back to the active theme's Style / Color singletons.

Both tokens expose one interface, so the widget never branches on which bar is running. If you want to re-theme the widget globally, edit HostTokens.qml:

Token Default Used for
fontFamily bar.fontFamily every text node
labelSize Style.font.body pill percent, device names
captionSize Style.font.caption panel header, sub-labels
iconSize Style.space(15) device glyphs (pill + panel)
ink bar.foreground regular text / icons
seal bar.urgent charging / low accent
paper bar.background panel chrome

Layout

devicebattstats/
├── manifest.json          plugin manifest (kinds: service, bar-widget)
├── README.md
├── qml/
│   ├── Panel.qml          bar-widget entry point: Ui.Panel root (pill + popup)
│   ├── Service.qml        service entry point (poller process, shared state)
│   ├── DevicePill.qml     bar pill (glyph + percent), click-target registration
│   ├── HostTokens.qml     bar-theming adapter (Shibumi + stock interface)
│   ├── IconText.qml       Material Symbols Rounded text (FILL axis)
│   └── DeviceBattery.js   pure data helpers (normalize, glyphs, summary)
└── scripts/
    ├── device-battery.sh  collector wrapper (bash)
    └── device-battery.py  collector (BlueZ, sysfs, upower, Razer)

How it works with the shell

  • The widget resolves its service through bar.shell.serviceFor("dev.deoxizn.devicebattstats").
  • The pill registers itself as a click target with the host bar, so popup click-through forwarding, tooltips, and the open-panel indicator work on any bar that follows Quattro's bar-widget contract.
  • The popup is a Ui.Panel + KeyboardPanel. On the Shibumi bar the host's WidgetSlot hosted-panel adapter repaints the card with Shibumi chrome automatically. For that to work the widget's mounted root is the panel and the KeyboardPanel is its direct child — don't wrap it in a Loader or an inner Item, or the adapter can't find it and the popup falls back to the un-hosted bottom-edge card.