Skip to content

Repository files navigation

ZMK Battery Bar icon

ZMK Battery Bar

A macOS menu bar app that displays battery levels from ZMK split keyboards via BLE.

Screenshot

macOS 14+ Swift 6.0 Release License: MIT

Features

  • Two-line status bar display — Shows Central (C) and Peripheral (P) battery levels with custom-drawn battery icons
  • BLE Battery Service — Reads battery levels via standard BLE Battery Service (0x180F) with notification subscription
  • Auto Central/Peripheral detection — Uses BLE descriptor (User Description) to identify each half of the split keyboard
  • Multiple keyboard support — Register and switch between multiple ZMK keyboards
  • Customizable side labels — Override the default C / P menu bar labels with L / R per keyboard to match your physical left/right layout
  • Single line layout — Optionally show both battery levels on one line (e.g. C50% P50%)
  • Hide battery icon — Optionally hide the battery icons and show only the labels and percentages
  • Swap battery positions — Optionally swap the order of the two battery rows in the menu bar
  • Auto-reconnect — Automatically reconnects with exponential backoff when the keyboard disconnects
  • Launch at Login — Optional auto-start via SMAppService

Requirements

  • macOS 14 (Sonoma) or later
  • A ZMK-powered split keyboard with BLE Battery Service enabled

Install

brew install --cask itouuuuuuuuu/tap/zmk-battery-bar

Usage

  1. Launch ZMK Battery Bar — it appears in the menu bar with C and P battery levels
  2. Click the status bar item to open the popover
  3. If no keyboard is connected, click Add Keyboard... to scan for BLE devices
  4. Select your ZMK keyboard from the discovered devices list
  5. Battery levels update automatically via BLE notifications

Customizing side labels

The menu bar shows C (Central) and P (Peripheral) by default. To display L / R instead:

  1. Open the popover and locate the [ L ][ R ] buttons on the right of each battery row
  2. Tap L or R on the Central row to assign that letter to the central side (the peripheral row flips automatically)
  3. Tap the same letter again to revert to C / P

Labels are stored per keyboard, so each registered keyboard keeps its own assignment.

How It Works

  1. On launch, the app connects to the previously saved keyboard via retrievePeripherals(withIdentifiers:)
  2. It discovers the BLE Battery Service (0x180F) and Battery Level Characteristic (0x2A19)
  3. Descriptor 0x2901 (User Description) is read to determine Central vs Peripheral
  4. Battery level notifications are subscribed to for real-time updates, with 60-second polling as fallback
  5. The status bar icon is rendered as an NSImage using SwiftUI ImageRenderer

ZMK Configuration

If the Peripheral battery level is not displayed, add the following to your Central side's .conf file:

CONFIG_ZMK_BATTERY_REPORTING=y
CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_PROXY=y
CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING=y

Development

# Debug build
swift build

# Build and launch local .app bundle (ad-hoc signing)
./scripts/build-app.sh
open "build/ZMK Battery Bar.app"

# Install to /Applications
cp -r "build/ZMK Battery Bar.app" /Applications/

# Release .app bundle with Developer ID signing
./scripts/build-app.sh "Developer ID Application: Your Name (TEAMID)" "1.0.0"

Release

Releases are automated via GitHub Actions. Pushing a version tag triggers the full pipeline:

git tag v1.2.0
git push origin v1.2.0

This will automatically:

  1. Build the app in release mode
  2. Sign with Developer ID certificate (hardened runtime)
  3. Notarize with Apple and staple the ticket
  4. Create a GitHub Release with the signed zip
  5. Update the Homebrew Cask with the new version and SHA256

Required GitHub Secrets

Secret Description
DEVELOPER_ID_CERTIFICATE_BASE64 Base64-encoded .p12 certificate
DEVELOPER_ID_CERTIFICATE_PASSWORD Password for the .p12 file
APPLE_ID Apple ID email for notarization
APPLE_ID_PASSWORD App-specific password for notarization
APPLE_TEAM_ID Apple Developer Team ID
HOMEBREW_TAP_TOKEN GitHub PAT with write access to homebrew-tap repo

Manual Release (without CI)

export APPLE_ID="your@email.com"
export APPLE_ID_PASSWORD="xxxx-xxxx-xxxx-xxxx"
export APPLE_TEAM_ID="XXXXXXXXXX"
./scripts/release.sh 1.2.0 "Developer ID Application: Your Name (TEAMID)"

Architecture

Directory Description
Sources/.../App/ Entry point, AppDelegate, NSStatusItem, NSPanel popover
Sources/.../BLE/ CoreBluetooth manager, device discovery, battery reading
Sources/.../Views/ SwiftUI views (status bar, popover, keyboard list)
Sources/.../Models/ BatteryState (@Observable), AppSettings (UserDefaults), KeyboardDevice
Sources/.../Utilities/ Launch at login (SMAppService wrapper)

Sponsorship

ZMK Battery Bar is free and open source. If it's been useful to you, you can support continued development via the Sponsor button at the top of this repository, or directly at https://github.com/sponsors/itouuuuuuuuu.

License

MIT

About

No description, website, or topics provided.

Resources

Stars

74 stars

Watchers

2 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages