The tray's Controls panel, and hammunition services¶
A field station spends most of its day with something running that nobody is using: a GPS receiver drawing power, a cellular modem looking for a tower, a Bluetooth controller, the service that serves your position to the map. This guide is the one place that says how to switch those on and off, from the tray or from a terminal, and what each switch asks of you.
Everything here is a front end for one small root helper,
/usr/local/libexec/hammunition-devctl, which belongs to
hammunition-tray. The tray,
hammunition hardware park, hammunition services and the generated menu
entries all call that same helper through the same polkit action, so they
cannot disagree. Why that is safe, and what it writes, is
Device power control; this page is the
how-to.
What you need first¶
-
The engine's hardware step, once:
It writes the two lists the helper reads, the devices it may park and the system services it may start and stop (the helper's lists). Without them the tray shows no device switches and no system services. 2. A tray, for your desktop. The
stationprofile carries both; each defers itself by name on a desktop it does not serve (Desktops).Desktop Install Where the switches are KDE Plasma hammunition install hammunition-traythe Hammunition Devices applet, added to the panel or the system tray by hand: nothing places it for you Xfce, LXQt, LXDE, MATE, Cinnamon hammunition install hammunition-tray-qta tray icon with a menu GNOME, COSMIC none yet use the terminal commands below Either unit also installs the helper and the polkit action, printed in the plan; the install asks one
yesthat--yescannot answer when the engine's virtualenv belongs to a single account, and refuses when any account can write it. 3. A polkit authentication agent running in your session, which every desktop above provides. Without one the password prompt never appears; see the pkexec note.
After installing, Plasma keeps the old applet loaded until
systemctl --user restart plasma-plasmashell or your next login.
The Controls panel¶
One panel, three groups, worded the same on Plasma and in the Qt tray.
| Group | One row per | What a switch does |
|---|---|---|
| Devices | catalogued device that is attached and parkable: the GPS receiver, a cellular modem, a Bluetooth controller, a camera | Park detaches it so the kernel drops its interfaces and its port can suspend; wake brings it back. A device kept off across reboots reads "kept off"; one unplugged while kept offers Forget |
| Services | service the helper controls | a switch for running and a Start at login checkbox |
| Radios | mobile broadband (WWAN), Wi-Fi, Bluetooth | switches your session's own radio, the way nmcli radio and bluetoothctl power do |
The Time section below the groups is the GPS clock's mode, covered in GPS time on ntpsec.
Which services appear. The helper's allow-list: gpsd (the GPS daemon's
socket), time (ntpsec or chrony, whichever your machine has),
gps-resume (re-adds the receiver to gpsd after a suspend), and any user
service a catalog unit registered: gps-tether once you have
installed the tether, and rig
once you have installed rig-service (Rig control). A
service whose unit is not installed is still listed, says not installed and
cannot be switched, so you can tell "off" from "never installed".
Which switch asks for a password¶
Reading is never privileged: the panel polls the helper every few seconds with no prompt. Changing something:
| You switch | Password prompt |
|---|---|
| a device (park, wake, forget) | one polkit prompt |
| the clock's mode | one polkit prompt |
a system service, running or at login (gpsd, time, gps-resume) |
one polkit prompt |
a user service, running or at login (gps-tether, rig) |
none: it is systemctl --user as you |
| a radio | none: polkit already lets your active session switch its own radios |
The prompt is pkexec showing the one action
com.chiefgyk3d.hammunition.devctl. Once you have authenticated, an active
session is not asked again for a short while (polkit's auth_self_keep), so
parking a device and waking it straight after asks once; the bench run below
saw exactly that. A dismissed or
refused prompt leaves the switch where it was: nothing was written.
A switch moves at once to what you asked for, and the next poll replaces that with what the helper really reports. If the helper could not do it, the switch goes back and its one line of reason is shown. While a command runs, every switch is disabled.
Update hammunition-tray¶
If a group says update hammunition-tray, the installed helper is older than
the panel (it answers contract 1's services and radio verbs only from
hammunition-tray 0.5.0). Re-run hammunition install hammunition-tray (or
-qt). If the whole panel says Device control is not installed, the helper
is missing: the same install puts it back.
The same switches in a terminal¶
Every switch has a command, which is also what to use on GNOME, COSMIC or a headless station.
| In the panel | In a terminal |
|---|---|
| a device | hammunition hardware state, hardware park NAME, hardware wake NAME (power control) |
| the clock's mode | hammunition time, hammunition time mode MODE |
| a service | hammunition services, services start\|stop\|enable\|disable NAME |
| a radio | nmcli radio wwan on\|off, nmcli radio wifi on\|off, bluetoothctl power on\|off (the helper wraps exactly these; the engine has no verb for them) |
hammunition services lists each service and what it is doing:
hammunition services # the list; no password
hammunition services start gps-tether # a user service: no password
hammunition services stop gpsd --dry-run # print the helper call, then stop
hammunition services enable time # a system service: one polkit prompt
- The engine asks the installed helper and never runs
systemctlitself. It passes a name from the list, never a unit; the helper looks the unit up in/etc/hammunition/devctl-services.yaml(system) or~/.config/hammunition/devctl-services.yaml(your own). - It reads the result back from the helper afterwards. A start that ends
failed, a stop that leaves the service running, or an enable that does not readenabledis reported as unverified and exits1. A name the helper does not list, or a unit that is not installed, exits2before any prompt. A dismissed prompt exits3. --jsonis available on the list only, as aservicesdocument (JSON interface); the four verbs change the machine and have no JSON form.
The full flag list is in the command line reference.
When it does not work¶
- The switch does nothing and no prompt appears. No polkit agent is
running in your session, or you are on a bare SSH session. Run
hammunition hardware park NAMEfrom a desktop terminal instead; the power-control page explains exit 126 and 127. hammunition servicessays the helper "has noservicesverb (it predates contract 1)". An older helper is installed (for example the onehardware applywrote in an earlier release). Runhammunition install hammunition-trayto bring in the current one, then check/usr/local/libexec/hammunition-devctl --version, which should printhammunition-devctl contract 1.- The Devices group is empty. Nothing parkable is attached, or
hammunition hardware applyhas not written the devices list. Compare withhammunition hardware state, which reads the same. - A service shows not installed. Its unit is not on this machine:
hammunition install gps-tetherorrig-service, or, fortimeon a machine whose daemon issystemd-timesyncd, see Time and position. - The clock modes are greyed. The machine's clock daemon is not ntpsec
(Debian 13, Ubuntu 24.04 and Mint ship
systemd-timesyncd, which cannot read a GPS; Ubuntu 26.04 ships chrony, which thechronyunit configures, Time and position); the panel says why.
What was measured, and what was not¶
Measured (field laptop, 2026-09-27, bench session 10): parking and waking
a USB GPS receiver from the Plasma applet: one KDE password prompt for the
park, none for the wake inside the auth_self_keep window, gpsd releasing
the device and taking it back within a second, a 3D fix again 74 seconds after
the wake (bench record). That run
used the engine's own copy of the helper and the applet before the Controls
panel and hammunition-tray 0.5.0's helper existed.
Measured (field laptop, 2026-10-02 and 03, bench session 13): the
engine's install of the tray's helper as root on a real machine, which left
hammunition-devctl contract 1 answering --version and the applet's
directory in place; the helper's read-only views against the real system
(state listing four devices, radio state through nmcli and
bluetoothctl, services state), and hammunition services listing gpsd,
time, the resume step and the tether. The applet survived a reboot on disk
and logged no QML error of its own.
Not measured. The Controls panel on screen, and every change made from
it: a service started or stopped, a radio toggled, a polkit prompt for a
system service, and whether a modem ModemManager has disabled is still listed
by nmcli (open). That Plasma lists an applet placed without a .deb has
not been seen either. The Xfce, LXQt, LXDE, MATE and Cinnamon trays, and
every park of a modem, a Bluetooth controller or a camera are unrun. A device
kept off across a reboot, which the panel shows as "kept off", is built and
has not been rebooted on hardware. Treat each as an expectation from the
design until the bench page says otherwise.