Code Monkey home page Code Monkey logo

batt's Introduction

👆↖️ Note: use table of contents of quickly navigate to the section you want.

batt

Go ChecksBuind Test Binary

batt is a tool to control battery charging on Apple Silicon MacBooks.

Why do you need this?

This article might be helpful. TL;DR: keep your battery at 80% or lower when plugged in, and discharge it as shallowly as feasible.

Previously, before optimized battery charging is introduced, MacBooks are known to suffer from battery swelling when they are kept at 100% all the time, especially the 2015s. Even with optimized battery charging, the effect is not optimal (described below).

batt can effectively alleviate this problem by limiting the battery charge level. It can be used to set a maximum charge level. For example, you can set it to 80%, and it will stop charging when the battery reaches 80%. Once it reaches the predefined level, your computer will use power from the wall only, leaving no strain on your battery.

Quick link to installation guide.

Features

batt tried to keep as simple as possible. Charging limiting is the only thing to care about for most users:

  • Limit battery charge, with a lower and upper bound, like ThinkPads. Docs

However, if you are nerdy and want to dive into the details, it does have some advanced features for the computer nerds out there :)

  • Control MagSafe LED (if present) according to charge status. Docs
  • Cut power from the wall (even if the adapter is physically plugged in) to use battery power. Docs
  • It solves common sleep-related issues when controlling charging. Docs1 Docs2

How is it different from XXX?

It is free and opensource. It even comes with some features (like idle sleep preventions and pre-sleep stop charging) that are only available in paid counterparts. It comes with no ads, no tracking, no telemetry, no analytics, no bullshit. It is open source, so you can read the code and verify that it does what it says it does.

It is simple but well-thought. It only does charge limiting and does it well. For example, when using other free/unpaid tools, your MacBook will sometimes charge to 100% during sleep even if you set the limit to, like, 60%. batt have taken these edge cases into consideration and will behave as intended (in case you do encounter problems, please raise an issue so that we can solve it). Other features is intentionally limited to keep it simple. If you want some additional features, feel free to raise an issue, then we can discuss this.

It is light-weight. As a command-line tool, it is light-weight by design. No electron GUIs hogging your system resources. However, a native GUI that sits in the menubar is a good addition.

But macOS have similar features built-in, is it?

Yes, macOS have optimized battery charging. It will try to find out your charging and working schedule and prohibits charging above 80% for a couple of hours overnight. However, if you have an un-regular schedule, this will simply not work. Also, you lose predictability (which I value a lot) about your computer's behavior, i.e., by letting macOS decide for you, you, the one who knows your schedule the best, cannot control when to charge or when not to charge.

batt can make sure your computer does exactly what you want. You can set a maximum charge level, and it will stop charging when the battery reaches that level. Therefore, it is recommended to disable macOS's optimized charging when using batt.

Installation

Currently, it is command-line only. Some knowledge of the command-line is recommended. A native GUI is possible but not planned. If you want to build a GUI, you can ask me to put a link here to your project

  1. (Optional) If you are lazy, there is an installation script to help you get the first 3 steps done (Internet connection required). Put this in your terminal: bash <(curl -fsSL https://github.com/charlie0129/batt/raw/master/hack/install.sh). You may need to provide your password (to control charging). This will download and install the latest stable version for you, then you can skip to step 5.
  2. Get the binary. For stable and beta releases, you can find the download link in the release page. If you want development versions with the latest features and bug fixes, you can download prebuilt binaries from GitHub Actions (has a retention period of 3 months and you need to chmod +x batt after extracting the archive) or build it yourself .
  3. Put the binary somewhere safe. You don't want to move it after installation :). It is recommended to save it in your $PATH, e.g., /usr/local/bin, so you can directly call batt on the command-line. In this case, the binary location will be /usr/local/bin/batt.
  4. Install daemon using sudo batt install. If you do not want to use sudo every time after installation, add the --allow-non-root-access flag: sudo batt install --allow-non-root-access. To uninstall: please refer to How to uninstall?
  5. In case you have GateKeeper turned on, you will see something like "batt is can't be opened because it was not downloaded from the App Store" or "batt cannot be opened because the developer cannot be verified". If you don't see it, you can skip this step. To solve this, you can either 1. (recommended) Go to System Preferences -> Security & Privacy -> General -> Open Anyway; or 2. run sudo spctl --master-disable to disable GateKeeper entirely.
  6. Test if it works by running sudo batt status. If you see some status info, you are good to go!
  7. Time to customize. By default batt will set a charge limit to 60%. For example, to set the charge limit to 80%, run sudo batt limit 80.
  8. As said before, it is highly recommended to disable macOS's optimized charging when using batt. To do so, open System Preferences, go to Battery, and uncheck Optimized battery charging.

Notes:

  • If your current charge is above the limit, your computer will just stop charging and use power from the wall. It will stay at your current charge level, which is by design. You can use your battery until it is below the limit to see the effects.
  • You can refer to Usage for additional configurations. Don't know what a command does? Run batt help to see all available commands. To see help for a specific command, run batt help <command>.
  • To disable the charge limit, run sudo batt limit 100.
  • How to uninstall? How to upgrade?

Finally, if you find batt helpful, stars ⭐️ are much appreciated!

Usage

Limit battery charge

Make sure your computer doesn't charge beyond what you said.

Setting the limit to 10-99 will enable the battery charge limit, limiting the maximum charge to somewhere around your setting. However, setting the limit to 100 will disable the battery charge limit. If you want to charge your MacBook to 100% or revert any changes, you can simply set the limit to 100%.

By default, batt will set a 60% charge limit.

To customize charge limit, see batt limit. For example,to set the limit to 80%, run sudo batt limit 80. To disable the limit, run sudo batt limit 100.

Enable/disable power adapter

Cut or restore power from the wall. This has the same effect as unplugging/plugging the power adapter, even if the adapter is physically plugged in.

This is useful when you want to use your battery to lower the battery charge, but you don't want to unplug the power adapter.

To enable/disable power adapter, see batt adapter. For example, to disable the power adapter, run sudo batt adapter disable. To enable the power adapter, run sudo batt adapter enable.

Check status

Check the current config, battery status, and charging status.

To do so, run sudo batt status.

Advanced

These advanced features are not for most users. Using the default setting for these options should work the best.

Preventing idle sleep

Set whether to prevent idle sleep during a charging session.

Due to macOS limitations, batt will pause when your computer goes to sleep. As a result, when you are in a charging session and your computer goes to sleep, the battery charge limit will no longer function and the battery will charge to 100%. If you want the battery to stay below the charge limit, this behavior is probably not what you want. This option, together with disable-charging-pre-sleep, will prevent this from happening.

To prevent this, you can set batt to prevent idle sleep. This will prevent your computer from idle sleep while in a charging session. This will only prevent idle sleep, when 1) charging is active 2) battery charge limit is enabled. So your computer can go to sleep as soon as a charging session is over.

However, this does not prevent manual sleep. For example, if you manually put your computer to sleep or close the lid, batt will not prevent your computer from sleeping. This is a limitation of macOS. To prevent such cases, see disable-charging-pre-sleep.

To enable this feature, run sudo batt prevent-idle-sleep enable. To disable, run sudo batt prevent-idle-sleep disable.

Disabling charging before sleep

Set whether to disable charging before sleep if charge limit is enabled.

Due to macOS limitations, batt will pause when your computer goes to sleep. As a result, when you are in a charging session and your computer goes to sleep, the battery charge limit will no longer function and the battery will charge to 100%. If you want the battery to stay below the charge limit, this behavior is probably not what you want. This option, together with prevent-idle-sleep, will prevent this from happening. prevent-idle-sleep can prevent idle sleep to keep the battery charge limit active. However, this does not prevent manual sleep. For example, if you manually put your computer to sleep or close the lid, batt will not prevent your computer from sleeping. This is a limitation of macOS.

To prevent such cases, you can use disable-charging-pre-sleep. This will disable charging just before your computer goes to sleep, preventing it from charging beyond the predefined limit. Once it wakes up, batt can take over and continue to do the rest work. It will only disable charging before sleep if battery charge limit is enabled.

To enable this feature, run sudo batt disable-charging-pre-sleep enable. To disable, run sudo batt disable-charging-pre-sleep disable.

Upper and lower charge limit

When you set a charge limit, for example, on a Lenovo ThinkPad, you can set two percentages. The first one is the upper limit, and the second one is the lower limit. When the battery charge is above the upper limit, the computer will stop charging. When the battery charge is below the lower limit, the computer will start charging. If the battery charge is between the two limits, the computer will keep whatever charging state it is in.

batt have similar features built-in (since v0.1.0). The charge limit you have set (using batt limit) will be used as the upper limit. By default, The lower limit will be set to 2% less than the upper limit. Same as using 'batt lower-limit-delta 2'. To customize the lower limit, use batt lower-limit-delta.

For example, if you want to set the lower limit to be 5% less than the upper limit, run sudo batt lower-limit-delta 5. So, if you have your charge (upper) limit set to 60%, the lower limit will be 55%.

Control MagSafe LED

Only available after (not including) v0.1.0. It is disabled by default.

Acknowledgement: @exidler

This setting can make the MagSafe LED behave like a normal device, i.e., it will turn green when charge limit is reached (not charging). By default, on a MagSafe-compatible device, the MagSafe LED will always be orange (charging) even if charge limit is reached and charging is disabled by batt, due to Apple's limitations. You cannot enable this feature on a non-MagSafe-compatible device.

One thing to note: this option is purely cosmetic. batt will still function even if you disable this option.

To enable MagSafe LED control, run sudo batt magsafe-led enable.

Check logs

Logs are directed to /tmp/batt.log. If something goes wrong, you can check the logs to see what happened. Or raise an issue with the logs attached, so we can debug together.

Building

You need to install command line developer tools (by running xcode-select --install) and Go (follow the official instructions here).

Simply running make in this repo should build the binary into ./bin/batt. You can then follow the upgrade guide to install it (you just use the binary you have built, not downloading a new one, of course).

Architecture

You can think of batt like docker. It has a daemon that runs in the background, and a client that communicates with the daemon. They communicate through unix domain socket as a way of IPC. The daemon does the actual heavy-lifting, and is responsible for controlling battery charging. The client is responsible for sending users' requirements to the daemon.

For example, when you run sudo batt limit 80, the client will send the requirement to the daemon, and the daemon will do its job to keep the charge limit to 80%.

Motivation

I created this tool simply because I am not satisfied with existing tools 😐.

I have written and using similar utils (to limit charging) on Intel MacBooks for years. Just since recently, I got hands on an Apple Silicon MacBook (yes, it is 2023, 2 years later since it is introduced 😅 and I just got one). The old BCLM way to limit charging doesn't work anymore. I was looking for a tool to limit charging on M1 MacBooks.

I have tried some alternatives, both closed source and open source, but I kept none of them. Some paid alternatives' licensing options are just too limited 🤔, a bit bloated, require periodic Internet connection (hmm?) and are closed source. It doesn't seem a good option for me. Some open source alternatives just don't handle edge cases well and I encountered issues sometimes especially when sleeping (as of Apr 2023).

I want a simple tool that does just one thing, and does it well -- limiting charging, just like the Unix philosophy. It seems I don't have any options but to develop by myself. So I spent a weekend developing this tool, so here we are! batt is here!

FAQ

How to uninstall?

  1. Run sudo batt uninstall to remove the daemon.
  2. Remove the config by sudo rm /etc/batt.json.
  3. Remove the batt binary itself by sudo rm $(where batt).

How to upgrade?

Automatic:

Updates to the latest stable version. If you want to use other versions (beta, development), please use the manual method.

bash <(curl -fsSL https://github.com/charlie0129/batt/raw/master/hack/install.sh)

Manual:

  1. Run sudo batt uninstall to remove the old daemon.
  2. Download the new binary. For stable and beta releases, you can find the download link in the release page. If you want development versions with the latest features and bug fixes, you can download prebuilt binaries from GitHub Actions (has a retention period of 3 months and you need to chmod +x batt after extracting the archive) or build it yourself .
  3. Replace the old batt binary with the downloaded new one. sudo cp ./batt $(where batt)
  4. Run sudo batt install to install the daemon again. Although most config is preserved, some security related config is intentionally reset during re-installation. For example, if you used --allow-non-root-access when installing previously, you will need to use it again like this sudo batt install --allow-non-root-access.

Why is it Apple Silicon only?

You probably don't need this on Intel :p

On Intel MacBooks, you can control battery charging in a much, much easier way, simply setting the BCLM key in Apple SMC to the limit you need, and you are all done. There are many tools available. For example, you can use smc-command to set SMC keys. Of course, you will lose some advanced features like upper and lower limit.

However, on Apple Silicon, the way how charging is controlled changed. There is no such key. Therefore, we have to use a much more complicated way to achieve the same goal, and handle more edge cases, hence batt.

Will there be an Intel version?

Probably not. batt was made Apple-Silicon-only after some early development. I have tested batt on Intel during development (you can probably find some traces from the code :). Even though some features in batt are known to work on Intel, some are not. Development and testing on Intel requires additional effort, especially those feature that are not working. Considering the fact that Intel MacBooks are going to be obsolete in a few years and some similar tools already exist (without some advanced features), I don't think it is worth the effort. If you are interested in developing an Intel version, feel free to raise a PR.

Why does my MacBook stop charging after I close the lid?

TL,DR; This is intended, and is the default behavior. It is described here. You can turn this feature off by running sudo batt disable-charging-pre-sleep disable (not recommended, keep reading).

But it is suggested to keep the default behavior to make your charge limit work as intended. Why? Because when you close the lid, your MacBook will go into forced sleep, and batt will be paused by macOS. As a result, batt can no longer control battery charging. It will be whatever state it was before you close the lid. This is the problem. Let's say, if you close the lid when your MacBook is charging, since batt is paused by macOS, it will keep charging, ignoring the charge limit you have set. There is no way to prevent forced sleep. Therefore, the only way to solve this problem is to disable charging before sleep. This is what batt does. It will disable charging just before your MacBook goes to sleep, and re-enable it when it wakes up. This way, your Mac will not overcharge during sleep.

Not that you will encounter this forced sleep only if you, the user, forced the Mac to sleep, either by closing the lid or selecting the Sleep option in the Apple menu. If your Mac decide to sleep by itself, called idle sleep, e.g. when it is idle for a while, in this case, you will not experience this stop-charging-before-sleep situation.

So you suggested not turning of this feature. But What if I MUST let my Mac charge during a forced sleep without turing off disable-charging-pre-sleep, even if it may charge beyond the charge limit? This is simple, just disable charge limit by setting it to 100% sudo batt limit 100. This way, when you DO want to enable charge limit again, disable-charging-pre-sleep will still be there to prevent overcharging. The rationale is: when you want to charge during a forced sleep, you actually want heavy use of your battery and don't want ANY charge limit at all, e.g. when you are on a long outside-event, and you want to charge your Mac when it is sitting in your bag, lid closed. Setting the charge limit to 100% is equivalent to disabling charge limit. Therefore, most batt features will be turned off and your Mac can charge as if batt is not installed.

Why does it require root privilege?

It writes to SMC to control battery charging. This does changes to your hardware, and is a highly privileged operation. Therefore, it requires root privilege.

It is also possible to run it without sudo. But I decided not to, because I want to make sure only you, the superuser, can control your computer, and to prevent accidental misuse.

If you want to use the cli without sudo, e.g. sudo batt limit 80, you can install the daemon with --allow-non-root-access flag, i.e., sudo batt install --allow-non-root-access. This will allow non-root users to access the daemon. However, this is not recommended from a security perspective.

If you are concerned about security, you can check the source code here to make sure it does not do anything funny.

Why is it written in Go and C?

Since it is a hobby project, I want to balance effort and the final outcome. Go seems a good choice for me. However, C is required to register sleep and wake notifications using Apple's IOKit framework. Also, Go don't have any library to r/w SMC, so I have to write it myself (charlie0129/gosmc). This part is also mainly written in C as it interacts with the hardware and uses OS capabilities. Thankfully, writing a library didn't slow down development too much.

Why is there so many logs?

By default, batt daemon will have its log level set to debug for easy debugging. The debug logs are helpful when reporting problems since it contains useful information. So it is recommended to keep it as debug. You may find a lot of logs in /tmp/batt.log after you use your Mac for a few days. However, there is no need to worry about this. The logs will be cleaned by macOS on reboot. It will not grow indefinitely.

If you believe you will not encounter any problem in the future and still want to set a higher log level, you can achieve this by:

  1. Stop batt: sudo launchctl unload /Library/LaunchDaemons/cc.chlc.batt.plist (batt must be stopped to change config so you can't skip this step)
  2. Use your preferred editor to edit /Library/LaunchDaemons/cc.chlc.batt.plist and change the value of -l=debug to your preferred level. The default value is debug.
  3. Start batt again: sudo launchctl load /Library/LaunchDaemons/cc.chlc.batt.plist

Acknowledgements

batt's People

Contributors

charlie0129 avatar exidler avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

batt's Issues

Unable to set higher level logs

First of all, I'm using automatic setup utility to install the batt (step 1), then go to "/Library/LaunchDaemons/cc.chlc.batt.plist", modify the file. Then use "sudo batt install" to install the daemon, I would like to ask what the normal procedure should be?

Clamshell mode?

Hi,

does this work with clamshell mode? I currently have charging at 100%, but I wish it to discharge to 80% despitebeing connected to the adapter. However, my laptop is in clamshell mode (closed and connected to external monitor), which, when I disable the adapter, it goes to sleep immediately.

Unable to charge the iphone

My Mac is connected to the display screen, but when the battery level is higher than the limit, my phone cannot charge while connected to the Mac.
Please tell me how to resolve this situation, it is very important to me, and I would greatly appreciate your help.

Sleepcallback.go: deregister for sleep/wake notifications during shutdown

By adding an additional info string:

diff --git a/sleepcallback.go b/sleepcallback.go
index a3bbcc8..6285739 100644
--- a/sleepcallback.go
+++ b/sleepcallback.go
@@ -161,5 +161,6 @@ func listenNotifications() error {
 	if int(C.ListenNotifications()) != 0 {
 		return fmt.Errorf("IORegisterForSystemPower failed")
 	}
+	logrus.Info("listenNotifications() has returned.")
 	return nil
 }

it is clear that listenNotifications() never returns in the happy path, not even during shutdown. This means the sleep listener CFRunLoop() was simply terminated, not given a chance to cleaned up: as per pwr_mgt/IOPMLib.h, the ports listening for sleep notifications "must" be closed by the caller. The code for cleaning up those also appeared in QA1340 listing 4. It's a memory leak.

You may have to investigate passing around the CFRunLoopRef of that thread to the Go side, so as to CFRunLoopStop it during exit, after which CFRunLoop() returns and clean up codes can be run. Or attach another source that sets an exit flag. Doesn't seem simple.


On a side note, IOPMLib.h mentions that kIOMessageSystemWillPowerOn is sent before most hardware has powered up, so if you print a log there, it would probably block until hard disks come back online. Maybe you can remove that callback.

Battery charge stuck at 100%

On macbook air M2, when the daemon is running, the charge shows 100%, even when it's completely unplugged and been running off the battery for a while.

Any way to debug what's causing that, logs also show battery charge at 100%:
time="2023-04-16 09:44:24.906" level=debug msg="batteryCharge=100, limit=75, chargingEnabled=true, isPluggedIn=false, maintainedChargingInProgress=false"

Will it continue to charge when it is turned off, is this intentional

this is a good project,but i have some questions .The charge will continue when I turn it off, and when I turn it on again, the charge is over 80, which makes me have to disconnect the charger to make it lower than 80, although I can do this by the command sudo pmset -b sleep 0; sudo pmset -b disablesleep 1 to shut down the screen external display still works,(im using the external display ) is there a better solution?
thanks.

Auto calibration support

Thanks for the neat and clean package.

It would be excellent to have a system where users can set an interval, let's say 7-days. Afterward, every 7 days, it will automatically charge up to 100 (no matter the limit define by user), then discharge to 15, and finally recharge to the user-defined limit.

The flow will be like.

  1. Keep track of the last calibration.
  2. Check if the last calibration was 7 days ago or more.
  3. If it's been more than 7 days, set the limit to 100%.
  4. When it reaches a 100% charge, disable the adapter.
  5. When it drops to 15%, enable the adapter.
  6. Set the limit to the user-defined value.
  7. Update the last calibration time.

Better limitter

Classical charge limiter have 2 levels:

  1. level when stop charging (currently batt named limit)
  2. level when start charging (less that 1.)

When we have battery charged up to limit 1.) and still does not discharged to level 2.) charging does not started
This logic prevent unnecessary small charing

Can you add this functionality?

Push to brew

Hi, it's a nice project with high user-friendly commandes.

I am wondering if you will push your tool to brew, then it will be much convenience for those who use brew as the package manager.

Thanks.

emergency mode

I love this implementation for battery management and I am amazed by how neat the package is. Anyway there is one feature you could consider adding to future versions:

Disabling any feature in this script seems a permanent operation, but a temporary solution might be beneficial, especially in a "scheduled work with no external power source available" scenario, where a 100% full charge is preferred to maximize the battery life. I would suggest a new command to disable, for example the upper threshold, temporarily for once, and then restore the limit back to the user-defined value automatically in the next cycle.

Turn off Magsafe LED if limit is reached

I was wondering if we can choose between having the Magsafe LED in green or turning it off when limit is reached. It's a bit distracting to have it in green while battery is just maintained at 80%. Thank you.

Continuing to charge in Sleep Mode

Hi,

This little script/app has been great so far - nice work on this! But I notice that my mac did sometimes continue to charge in sleep mode with lid off. I set mine between 60%, and one time it charged to 77% and another to 61%. I wonder what the underlying issue is. Thanks in advance!

Is the `disable-charging-pre-sleep` always ON?

As the title says, is the disable charging before sleep is ON as long as batt is ON, even if setting limit to 100?
If so, I think it is not appropriate. Since if one will use the laptop off-grid, so that one will set limit to 100, then it is supposed to be charged as mush as possible.
May be another condition should be added to disable-charging-pre-sleep.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.