Skip to content

feat(battery): add force-discharge and manual toggles#7

Merged
harryisfish merged 1 commit into
mainfrom
feat/battery-cli-parity-main
Jun 10, 2026
Merged

feat(battery): add force-discharge and manual toggles#7
harryisfish merged 1 commit into
mainfrom
feat/battery-cli-parity-main

Conversation

@harryisfish

Copy link
Copy Markdown
Contributor

Summary

Rebuilds closed stacked PR #6 onto the current main after PR #4 was squash-merged.

  • add smctl battery charging on|off and smctl battery adapter on|off
  • add smctl battery maintain <limit> --force-discharge, backed by daemon-owned adapter cut/restore behavior
  • reassert adapter cut during one-shot battery discharge so firmware resets cannot silently stall the discharge
  • show IOKit time-to-empty/full estimates in battery status
  • add smctl daemon logs --last <range> as a thin wrapper around the smctl unified-log predicate
  • document the new CLI entries in English and Chinese README files

Changes added while rebuilding

  • persist battery.force_discharge in writeConfig, so later battery/fan writes do not drop the policy
  • normalize disabled limits (100/stop) so force-discharge cannot remain armed after charge limiting is stopped
  • add a daemon regression test for force-discharge config roundtrip and stop behavior

Tests

  • swift test --disable-sandbox — 89 tests, 0 failures
  • git diff --check
  • git fetch origin main --quiet && git merge-tree --write-tree --merge-base $(git merge-base HEAD origin/main) HEAD origin/main

…-discharge, time remaining, daemon logs

Feature parity pass against actuallymentor/battery v1.3.2, all verified
live on M2 MacBook Air:

- `battery charging on|off` and `battery adapter on|off`: expose the
  existing setChargingEnabled/setAdapterEnabled XPC calls in the CLI.
  When a maintain policy is active the CLI warns that the toggle may be
  overridden on the next evaluation (verified: it is, within seconds).
- `battery maintain N --force-discharge`: the policy actively discharges
  down to the band by cutting adapter power while above the upper bound,
  and restores it once inside. Persisted as battery.force_discharge in
  config.toml. The evaluation loop re-asserts the cut every period,
  which makes it self-healing against the firmware quirk below.
  Switching back to a plain maintain hands the adapter back (daemon-side
  transition, verified both directions).
- `battery status` shows time-to-empty/full from IOKit power sources
  (nil while the gauge is settling — mirrors pmset's "(no estimate)").
- `daemon logs [--last 1h]`: wraps `log show` with the smctl subsystem
  predicate.

Field finding that shaped the design: writing the charging-enable key
(CHTE) while an adapter cut (CHIE) is active made the SMC silently
re-enable adapter power — observed live, it stalled a one-shot
`battery discharge` forever at 79%. The one-shot discharge loop now
re-asserts the cut every poll, and force-discharge inherits immunity
from the policy loop (verified: triggering the same reset under
force-discharge is corrected within one XPC call).

PolicyEngine: BatteryObservation gains isAdapterEnabled (nil when
adapter control is unsupported — the policy then never touches the
adapter, which also keeps it out of the way of manual discharges).
New transition-matrix tests for the force-discharge branches.
@harryisfish harryisfish force-pushed the feat/battery-cli-parity-main branch from 5fc3499 to fa32095 Compare June 10, 2026 20:33
@harryisfish harryisfish merged commit da4c98e into main Jun 10, 2026
1 check passed
@leaperone-bot leaperone-bot deleted the feat/battery-cli-parity-main branch June 10, 2026 20:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants