Skip to content

Add reachability (Auto) keep-awake mode + battery-floor safety#49

Open
tomarai85 wants to merge 2 commits into
fuji-mak:mainfrom
tomarai85:feature/reachability-and-battery-floor
Open

Add reachability (Auto) keep-awake mode + battery-floor safety#49
tomarai85 wants to merge 2 commits into
fuji-mak:mainfrom
tomarai85:feature/reachability-and-battery-floor

Conversation

@tomarai85

Copy link
Copy Markdown

What

Two small, opt-in additions. Defaults are unchanged — with no configuration the app behaves exactly as it does today (Caps Lock keeps the Mac awake).

1. Keep-awake mode: Off / Caps Lock / Auto

Caps Lock stays the default and works exactly as today. Auto keeps the Mac awake automatically, independent of Caps Lock — for the case of closing the lid and driving the machine remotely (e.g. SSH from a phone), where you want it to stay reachable without remembering to toggle a key. Off is an explicit no-op.

2. Battery floor (opt-in, default 15%)

On battery, keep-awake is released at/below a threshold so the Mac can sleep before the battery fully drains — which, in the remote-use case, would also drop your access. It re-arms on AC, with hysteresis so it doesn't oscillate as the percentage jitters near the threshold.

Implementation notes:

  • BatteryReader reads the power source via IOKit (IOPSCopyPowerSourcesInfo) — no subprocess.
  • The decision is isolated in a pure BatteryFloorPolicy with 10 unit tests covering the floor boundary, the hysteresis latch, and the fail-safe cases (unreadable power → stay awake/reachable; on AC → keep; unknown percentage → keep).

Both are surfaced in the status menu (mode items + a Battery floor submenu) and the settings window, following the existing preference + localization patterns.

One small responsiveness change: the poll timer runs in .default instead of .common, so its periodic pmset -g verification never spawns a subprocess while a menu is being tracked.

Testing

swift test — all 24 tests green (14 existing + 10 new).

Notes

  • Fully backwards compatible; nothing changes unless you pick a non-default mode or enable the floor.
  • Happy to split this into two PRs (Auto mode / battery floor) if you'd rather review them independently, or to drop/adjust anything that doesn't fit Capsomnia's scope.

tomarai85 and others added 2 commits July 18, 2026 19:51
Two opt-in additions; defaults are unchanged, so existing behaviour is identical.

Keep-awake mode (Off / Caps Lock / Auto):
- Caps Lock stays the default and works exactly as today.
- Auto keeps the Mac awake automatically (independent of Caps Lock) -- for closing
  the lid and remote-controlling the machine (e.g. SSH from a phone), where you need
  it to stay reachable without remembering to toggle a key.

Battery floor (default 15%, opt-in, hysteresis-latched):
- On battery, release keep-awake at/below the threshold so the Mac can sleep before
  the battery fully drains (which would also drop remote access). Recovers on AC.
- BatteryReader reads the power source via IOKit; the decision is isolated in a pure
  BatteryFloorPolicy with 10 unit tests (boundary, hysteresis, unreadable/AC/unknown
  fail-safes -- unreadable power state stays awake, i.e. reachable).

Surfaced in the status menu (mode items + a Battery floor submenu) and the settings
window. The poll timer runs in .default (not .common) so its periodic pmset
verification never spawns a subprocess during menu tracking.

All 24 tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The PR describes both additions as opt-in with defaults unchanged, but
registerDefaults() shipped batteryFloorEnabled as true. On a stock install
that released keep-awake at 15% on battery even with Caps Lock held, which
is a behavior change upstream users never opted into.

Registering it as false makes the stated contract true: fresh installs
behave exactly as before, and the floor is something you turn on.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant