NO

ncr/omarchy-headphones

Developer tools
22 stars Quality 60 Trend 30MIT

Wireless headphones in the Omarchy bar — per-earbud battery and noise control

Overview

JBL  ·  Sony  ·  Samsung  ·  Nothing / CMF  ·  Soundcore  ·  Xiaomi  ·  OPPO  ·  Bose  ·  TOZO  ·  Huawei  ·  Redmi  ·  EarFun  ·  QCY Battery levels and noise-cancellation control for Bluetooth headphones, in the Omarchy bar. What it does · Supported headphones · In the panel · Settings · Gallery · Add your headphones JBL TUNE230NC TWS — @ncr Sony WH-CH720N — @ncr Soundcore Space 2 — @Sovego Nothing Ear (a) — @Jenesaispas69 Sony WH-1000XM5 — @huynguyendinhquang soundcore Space One Pro — @sasiruLK Sony WH-1000XM4 — @seth-reee Sony WH-1000XM6 — @f-iacono Samsung Galaxy Buds2 — @seth-reee Sony 1000X The Collexion — @KentoNion Xiaomi Buds 5 Pro — @KentoNion OPPO Enco Air3 Pro — @Aryan447 CMF Headphone Pro — @adilahmad17 Original capture; low-latency row partly cropped. Bose QC45 — @Driskol Sony WH-CH520 — @enobale CMF Buds 2 — @HanzGeeratz

README

Omaphones for Omarchy

JBL  ·  Sony  ·  Samsung  ·  Nothing / CMF  ·  Soundcore  ·  Xiaomi  ·  OPPO  ·  Bose  ·  TOZO  ·  Huawei  ·  Redmi  ·  EarFun  ·  QCY

Battery levels and noise-cancellation control for Bluetooth headphones, in the Omarchy bar.

Install — copy into a terminal and run:

omarchy plugin add https://github.com/ncr/omarchy-headphones --enable

Update an installed copy:

omarchy plugin update io.github.ncr.omaphones --yes && omarchy restart shell

Headphones not on the list? Add yours.

What it does · Supported headphones · In the panel · Settings · Gallery · Add your headphones

What it does

  • Battery level — per earbud and the case, or the single battery of over-ear headphones.
  • The bar icon is the meter — earbuds are drawn as a pair, a headset as headphones, filling up as they charge. Readable from the bar without opening anything.
  • Noise control — Off / ANC / Ambient / TalkThru, from the panel or a key. JBL, Sony, Samsung, Nothing / CMF, Soundcore, Xiaomi Buds 5 Pro, Redmi Buds 5 and 3 Pro, OPPO Enco Air3 Pro, EarFun Air Pro 4, Bose QC45 and Bose QC35 today; built to learn your brand. Sony and Soundcore add the ambient level with Focus on Voice or wind noise reduction; Nothing / CMF the ANC strength (Low / Mid / High / Adaptive) and a low-latency switch, and the Bose QC35 grades its ANC Low or High.
  • Pause when you take them off, resume when you put them back on — on a headset with a wear sensor (confirmed on the Sony WH-1000XM6). Only the players the sensor paused are resumed. Any headphones pause what is playing when they disconnect.
  • Several headphones at once — one icon per connected set, each with its own panel.
  • Low-battery notification that names the earbud.
  • Scriptable — omarchy-shell omaphones status, omarchy-shell omaphones setMode anc, and so on.

For AirPods use omapods instead: the same idea, built for Apple’s own protocol, and the plugin this one is modelled on.

Supported headphones

Device Battery Noise control Confirmed by
JBL TUNE230NC TWS (earbuds) left, right, case Off · ANC · Ambient · TalkThru @ncr
JBL Wave Buds 2 (earbuds) left, right, case Off · ANC · Ambient · TalkThru @carlosr-sh
Sony WH-CH720N (over-ear) one figure Off · ANC · Ambient (level, Focus on Voice) @ncr
Sony WH-1000XM5 (over-ear) one figure Off · ANC · Ambient (level, Focus on Voice) @huynguyendinhquang
Sony WH-1000XM6 (over-ear) one figure Off · ANC · Ambient (level, Focus on Voice) · wear pause/resume @f-iacono
Sony WH-1000XM4 (over-ear) one figure Off · ANC · Ambient @seth-reee
Sony 1000X The Collexion (over-ear) one figure Off · ANC · Ambient (level, Focus on Voice) @KentoNion
Samsung Galaxy Buds2 (earbuds) left, right, case Off · ANC · Ambient @seth-reee
Soundcore Space 2 (over-ear) one figure Off · ANC · Ambient (level, wind noise reduction) @Sovego
Xiaomi Buds 5 Pro (earbuds) one figure Off · ANC · Ambient @KentoNion
Redmi Buds 5 (earbuds) left, right, case Off · ANC · Transparency @quocldce-sys
Redmi Buds 3 Pro (earbuds) left, right, case Off · ANC · Transparency @osteotek
EarFun Air Pro 4 (earbuds) left, right, case (Fast Pair) Off · ANC · Ambient @UnFik
OPPO Enco Air3 Pro (earbuds) left, right, case Off · ANC · Ambient @Aryan447
Bose QC45 (over-ear) one figure ANC · Ambient (no Off) @Driskol
Bose QC35 (over-ear) one figure Off · ANC (Low / High) — no Ambient @pedrohfp
Nothing Ear (a) (earbuds) left, right, case Off · ANC (Low / Mid / High / Adaptive) · Ambient · low latency @Jenesaispas69
CMF Headphone Pro (over-ear) one figure Off · ANC (Low / Mid / High / Adaptive) · Ambient · low latency @adilahmad17
CMF Buds 2 (earbuds) left, right, case Off · ANC (Low / Mid / High / Adaptive) · Ambient · low latency @HanzGeeratz
Nothing Ear · Headphone (1) expected (one figure on Headphone (1)) expected — same protocol, per omarchy-nothing-ear —
soundcore Space One Pro (A3062, over-ear) one figure Off · ANC · Ambient (level, wind noise reduction) @sasiruLK
soundcore Life Q30 (A3028, over-ear) one figure (Fast Pair) Off · ANC · Ambient @kevinbsr
Sony WH-CH520 one figure (BlueZ) none — Sony lists no ANC/Ambient on this model, confirmed on hardware @enobale
TOZO NC9 Pro (earbuds) left, right, case Normal · ANC · Transparency · Reduce Wind Noise · Leisure · Adaptive @seth-reee
HUAWEI FreeBuds SE 2 (earbuds) left, right, case none — the model has no noise control @defkode
QCY H3 (over-ear) one figure (BlueZ) none — its Serial Port channel answers none of the QCY reads (see PROTOCOL.md) @carraly
other Fast Pair headphones expected —

CMF Headphone Pro’s connection, reconnect recovery and all panel controls were confirmed on hardware by its owner. See the test record and remaining limits.

Add your own headphones

Use /add-new-bridge in Codex or Claude, or ask your agent to read the shared skill. It first checks whether an existing bridge suffices, then guides capture, tests and owner validation.

Paste the text below into your AI coding agent (Claude Code, Codex, OpenCode…). It adds support for your headphones and opens a pull request here. How the existing protocols were found is written down in PROTOCOL.md.

Add support for my Bluetooth headphones to the Omaphones plugin for Omarchy and
open a pull request against `github.com/ncr/omarchy-headphones` with the result.
0. If it is not installed yet: `omarchy plugin add
   https://github.com/ncr/omarchy-headphones --enable --yes` (`--yes` because you
   have no terminal to confirm in). It lands in
   `~/.config/omarchy/plugins/io.github.ncr.omaphones`.
1. Connect the headphones and check `omarchy-shell omaphones status`. Find out
   what they serve: `bluetoothctl devices` for the address, `bluetoothctl info
   ` for the UUIDs — `df21fe2c-2515-4fdb-8886-f12c4d67927c` is the
   Google Fast Pair Message Stream (battery), `956c7b26-d49a-4ba8-b03f-b17d393cb6e2`
   is Sony MDR v2 and `96cc203e-5068-46ad-b32d-e316f5e069ba` Sony MDR v1 (both
   noise control), `2e73a4ad-332d-41fc-90e2-16bef06523f2` is Samsung SPPNew,
   `aeac4a03-dff5-498f-843a-34487cf133eb` is Nothing NT Link, a UUID
   starting `0cf12d31-fac3-4553-bd80-d6832e7` is Soundcore's vendor channel;
   JBL earbuds are probed over BLE by the plugin itself.
2. If battery and the mode row both already work, no bridge change is needed.
   Still bring my device's own capture, pin and confirmed capabilities from
   step 3, plus its README row and the screenshot from step 4.
3. Follow `.agents/skills/add-new-bridge/SKILL.md` in the repository.
   Read `AGENTS.md` in the plugin directory
   first: it is the map — which files a new model or a new brand touches,
   the bridge contract (`BRIDGE.md`), how a session is pinned
   (`tests/pins/`), and `tools/check`, the one command that runs everything
   a pull request is held to. Run `tools/check` until it passes; CI runs the
   same script on the pull request.

   The canonical examples are JBL TUNE230NC TWS and Sony WH-CH720N,
   prepared and hardware-tested by the maintainer @ncr on his own headphones.
   Read `docs/CANONICAL-TESTS.md`, their `*-canonical.json` pins, captured
   packets and shared fault/battery tests. Match their coverage for the
   capabilities my model actually offers: all mode replies, battery shape,
   malformed/partial data, unsolicited changes, silence vs disconnect,
   recovery and isolation from another device. Use my device's own frames;
   canonical means a coverage example, not permission to reuse their bytes.
   Record a live test with device-reported results and restored settings;
   clearly list anything untested or not applicable. Preserve all existing pins.
   These expanded requirements apply to new models and brands. Existing
   supported models keep their current coverage; do not require historical
   gaps to be filled. Changes to an existing model need tests for the changed
   behaviour and its owner's confirmation, while preserving its existing pins.

   Two rules hold whatever brand this is, because nobody has more than their
   own headphones — the maintainer cannot test mine and I cannot test
   anybody else's — and a pull request that breaks either will be sent back:

   **A new model may not change what an existing one is sent.** Somebody
   else's headphones work today on frames nobody here can retest. A model
   gets its own row (`MODELS` in its bridge) and its own pin file
   (`tests/pins//.json`, the frozen session of its owner's
   headphones); adding mine adds a row and a file, it does not edit another
   owner's. Where something must be decided, let it widen rather than
   narrow: prefer asking one more question to asking one fewer.

   **Ship only what you saw the headphones answer — no guessed bytes.** A
   variant from a vendor table that my headset never answered stays out, in
   the code and in `PROTOCOL.md` both. Keep the probe's output as
   `docs/captures/-.txt` and name it from the pin.

   Mind that any file written inside the plugin directory reloads the plugin
   at once — edit elsewhere and move files in, as the tools in `tools/` do.
4. Take the screenshot: `tools/gallery-shot ` — the `gallery-screenshot`
   skill in `.claude/skills/` has the steps — and add it to the Gallery at the
   bottom of `README.md` with the device name and my handle. The screenshot is
   part of the pull request.
5. Commit, push to a fork, and open the pull request.

Negative results are welcome too — a row saying what does not work saves the next person the afternoon.

In the panel

The icon is the meter:

Keys, while the panel is open (the panel lists them itself, bottom rows):

Key Does
o n a t Off · ANC · Ambient · TalkThru — only the modes this device has
w e d Reduce Wind Noise · Leisure · Adaptive (TOZO NC9 Pro) — modes of their own, not the Soundcore wind switch on f
[ ] Ambient level down / up (Sony 0-20, Soundcore 1-5)
f Focus on voice (Sony) / wind noise reduction (Soundcore) on / off
1 2 3 4 ANC strength: Low · Mid · High · Adaptive (Nothing / CMF) — turns ANC on at it
g Low latency on / off (Nothing / CMF)
r Ask the device again
, . Previous / next connected set
b v Bluetooth panel / Audio panel
tab esc Next panel / close

Click an icon to open its panel; with two sets connected, click the one you mean.

Everything is reachable over IPC — omarchy-shell omaphones :

Method Answers
status one line per device it follows, e.g. JBL TUNE230NC TWS · L 90% · R 100% · case 78%
battery · left · right · batteryCase a level 0-100, or -1
charging which parts say they are charging
mode · setMode off anc ambient talkthru; also wind leisure adaptive on TOZO NC9 Pro. Reads may say pending / unsupported; writes say ok / busy / unavailable.
ambientLevel · setAmbientLevel 0-20 (Sony), 1-5 (Soundcore)
ambientVoice · setAmbientVoice on|off on / off — Focus on voice (Sony), wind noise reduction (Soundcore)
ancLevel · setAncLevel low mid high adaptive (Nothing / CMF); setting one turns ANC on
latency · setLatency on|off on / off (Nothing / CMF)
worn worn / not worn, or unsupported for a device with no wear sensor
refresh ask the device again
open · close · toggle the panel

With more than one set connected, every method that reads or writes a device has a …For twin that names a device by a piece of its name, address or brand: modeFor sony, setModeFor anc jbl, openFor jbl.

The table is the useful subset — Service.qml answers a few more introspection calls (bleAddress, panelRect) for the tools in tools/.

What it deliberately does not do — connect, volume, MAC addresses, equalisers — lives in the stock panels a keypress away (b, v).

Settings

omarchy bar set io.github.ncr.omaphones (add --json for numbers and booleans), or the widget’s entry in ~/.config/omarchy/shell.json.

Key Default Meaning
deviceMatch "" Substring of name or address; empty shows every connected audio device, set it to follow only the matching ones.
useFastPair true Battery over Fast Pair. Off: battery from a supported noise-control bridge while useModeControl is on, otherwise BlueZ’s single figure if available. No Fast Pair reader or JBL mode row (its BLE address is announced there).
useModeControl true The mode row (noise control) and the link behind it.
lowBatteryThreshold 20 Urgent icon and notification from here down (5-50).
showPercentage false A written percentage instead of the drawn meter; costs a bar slot.
hideWhenDisconnected true Drop the widget while nothing is connected.
notifyLowBattery true Send the notification.
pauseMediaOnDisconnect true Pause playing media players when a device disconnects or comes off the ears, and resume the same players when it goes back on.

Credits

Licence

MIT. See LICENSE.

View the full README on GitHub

Recommended Tools

Try a different keyword or remove a filter.

Install

npx skillfish add ncr/omarchy-headphones
New to MCP? Start with the mcpskill.org guide →