Reading the battery level programmatically
February 14, 2009 – 14:25 Share on TwitterA beta-tester was complaining the application was draining its battery. To check if this was really the case, I needed to get the battery level with a better accuracy than just looking at the battery icon. So I used this little routine to check.
This routine is adapted from a post on MacRumors.com.
It uses non-approved calls, so don’t post your application with this code or you may be rejected!
The method I used was:
- Copy the IOPowerSources.h and IOPSKeys.h from the Simulator into my project
- Add the libIOKit.A.dylib from the iPhone to my project
To get a copy of the libIOKit.A.dylib without jailbreaking my iPhone, I used FSWalker from Nicolas Seriot, a nifty utility which creates a web server on your iPhone and allows you to navigate your iPhone file system with a web browser.
So launch FSWalker, and with your browser go to http://192.168.2.100:20000/System/Library/Frameworks/IOKit.framework/Versions/A (replace the IP address with the address of your iPhone).
Download the IOKit file and add it to your project with the name “libIOKit.A.dylib”.
#include "IOPowerSources.h"
#include "IOPSKeys.h"
- (double) batteryLevel
{
CFTypeRef blob = IOPSCopyPowerSourcesInfo();
CFArrayRef sources = IOPSCopyPowerSourcesList(blob);
CFDictionaryRef pSource = NULL;
const void *psValue;
int numOfSources = CFArrayGetCount(sources);
if (numOfSources == 0) {
NSLog(@"Error in CFArrayGetCount");
return -1.0f;
}
for (int i = 0 ; i < numOfSources ; i++)
{
pSource = IOPSGetPowerSourceDescription(blob, CFArrayGetValueAtIndex(sources, i));
if (!pSource) {
NSLog(@"Error in IOPSGetPowerSourceDescription");
return -1.0f;
}
psValue = (CFStringRef)CFDictionaryGetValue(pSource, CFSTR(kIOPSNameKey));
int curCapacity = 0;
int maxCapacity = 0;
double percent;
psValue = CFDictionaryGetValue(pSource, CFSTR(kIOPSCurrentCapacityKey));
CFNumberGetValue((CFNumberRef)psValue, kCFNumberSInt32Type, &curCapacity);
psValue = CFDictionaryGetValue(pSource, CFSTR(kIOPSMaxCapacityKey));
CFNumberGetValue((CFNumberRef)psValue, kCFNumberSInt32Type, &maxCapacity);
percent = ((double)curCapacity/(double)maxCapacity * 100.0f);
return percent;
}
return -1.0f;
}
Don't forget to remove the headers and libIOKit.A.dylib from your code before shipping!
17 Responses to “Reading the battery level programmatically”
great stuff really useful. thanks for this. i have bookmarked this at http://www.iphonekicks.com/objectivec/Reading_the_battery_level_programmatically
By Raj on Mar 24, 2009
nice one, I use this for my battery life tests.
By Marcelo Emmerich on Mar 29, 2009
I’ve read somewhere that the iPhone API only returns the battery level in 5% steps. Can you confirm if this is true or just a myth?
Thanks.
By Carlos Martins on Jul 3, 2009
Hi there,
I was wondering if someone could help me. This stuff is good and everyone else seems to be able to get it to work, I however can not. I have added the necessary files to my project, but when I run the code I get three errors. Could someone please tell me where the actual code has to be inserted for it to work, and also anything else that has to be done. The specific errors I am getting can be posted if needed. As you can probably tell I am a beginner to iphone programming!
Any help would be greatly appreciated!
By Stuart Forbes on Aug 17, 2009
Show me the errors, and I will try to help you.
By Stephan on Aug 17, 2009
Stuart,
Did you add the libIOKit.A.dylib to your project?
Anyway, my post was pre-3.0 SDK, so you should use the batteryLevel method of the UIDevice class:
float level = [[UIDevice currentDevice] batteryLevel];Before reading the battery level, you must enable the battery monitoring:
[[UIDevice currentDevice] setBatteryMonitoringEnabled:YES];Hope this helps,
By Stephan on Aug 18, 2009
Hi Stephan,
I didn’t understand!
“Don’t forget to remove the headers and libIOKit.A.dylib from your code before shipping!”.
Why should I do that? How can I do that? Can you help me this issue?
Thans!
By Hans on Dec 1, 2009
Just remove the libIOKit.A.dylib you’ve added to your project. Apple now has an automated tool to scan your code for private (ie not-approved methods) and will reject your app if it contains private methods.
By Stephan on Dec 2, 2009
But, if I remove the libIOKit.A.dylib I can’t use this routine and its APIs. Am I right? Or Is there another way to read the battery level programmatically without using the SDK 3.0 APIs(5%)? If I can’t use your routine because Apple wouldn’t allow private APIs, do you know any other way to do that (using C language for instance)?
Thanks for your help.
By Hans on Dec 2, 2009
If you dont use the 3.0 SDK, there is no approved way of reading the battery level.
By Stephan on Dec 2, 2009
Hello Stephan,
Is it possible to get the values for curCapacity and maxCapacity using this method, and use them in different calculations? I have tried using them, but I get the error ‘undeclared’.
By Stuart Forbes on Jan 5, 2010
I have sorted the undeclared problem. My apologies!
By Stuart Forbes on Jan 5, 2010
Hello Stephan,
How would I use a similar thing, but return maxCapacity and curCapacity individually rather than percent?
Thanks in advance,
Stu
By Stuart on Jan 10, 2010
Stuart,
Just change the definition of the method to:
- (NSDictionary *) batteryDataThen replace the
return percent;by
return [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:curCapacity], @"curCapacity", [NSNumber numberWithInt:maxCapacity], @"maxCapacity", nil];also replace the last line to
return nil;That should do the trick.
By Stephan on Jan 11, 2010
Thanks Stephan,
I have done this but get the error “incompatible types in return” at the other two instances of return 1.0f; Should these be return nil; also?
Before I used double level = [self batteryLevel]; when I was using your code to return a percent value. I then put the variable ‘level’ into the required label. How exactly would I put the value for curCapacity into a label now that NSDictionary is being used? I tried:
double level = [self batteryData];
curCapacity.text = [NSString stringWithFormat:@"%.2f %", curCapacity];
This did not work. Please note I changed:
– (double) batteryLevel;
In the .h file to:
- (NSDictionary *) batteryData;
Thanks once again for your help! I hope you can understand what I mean.
Stuart
By Stuart Forbes on Jan 25, 2010
Stuart,
You are right, you have to change all “return -1.0f” by “return nil”.
Better, you should replace all “return -1.0f” and the last line: “return nil” by:
return [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:-1], @”curCapacity”, [NSNumber numberWithInt:-1], @”maxCapacity”, nil];;
So in your code, you should do:
int level = [[[self batteryData] objectForKey:@”curCapacity”] intValue];
curCapacity.text = [NSString stringWithFormat:@"%d mAh", curCapacity];
Note that level is now an int, not a float.
HTH,
Stephan
By Stephan on Jan 26, 2010