Frederick wrote:
Does a laptop actually know there is no battery, or just that the battery level is zero (when the battery is physically removed)?
In short, yes.
On Windows, there is in fact a third state for GUID_ACDC_POWER_SOURCE when normal A/C - PoAc and battery - PoDc are unavailable and system is using a UPS [1] - PoDc. MSDN [2] is pretty nice.
Windows also knows if it has 1 battery or 2 batteries [3] (in theory, I suppose it could tell you there are more, but I've never seen that, I've personally used laptops with 0, 1 and 2 batteries -- one laptop, I could swap the CD bay for a battery, and I could remove the primary battery, and if I was stupid, I could trip over the power cord). It might even know if they're high capacity batteries.
GUID_ACDC_POWER_SOURCE PoAc (has A/C power) PoDc (is using a battery) PoHot (is using a UPS -- i.e. it's really unhappy and if you aren't critical you should please get off the system so it can safely power down) GUID_BATTERY_PERCENTAGE_REMAINING A percentage (granularity no better than 1%)
You can do better with IOCTL_BATTERY_QUERY_STATUS / BATTERY_STATUS [4], which can give you voltage, capacity, rate, and of course find out that the battery is really unhappy (BATTERY_CRITICAL).
Note that the IOCTL shows that one can generally get useful data in Time units on Windows instead of as a percentage. This argues in favor of an API which I described before of the form "I need to be able to do X minutes of work, let me know if this will be a problem".
GUID_SYSTEM_AWAYMODE Vista feature for Media PCs [5], in short the computer looks off to the user, but is still technically on. A DVR in such a state could continue to record programming, but shouldn't bother trying to send content to output devices (display, speakers). GUID_MONITOR_POWER_ON Primary display is on/off - if your application is running on the primary display, this is kinda useful to know about (stop using the GPU when this switches to off). PBT_APMBATTERYLOW Battery is low PBT_APMRESUMECRITICAL System is resuming after killing suspending because it ran out of power.
Since I'm doing the homework now, let's look at OS X too... The primary source for this content is IOKit/IOPowerSources [6].
The general content says that IOPSGetPowerSourceDescription will sometimes offer "Time Remaining To Empty", it seems to indicate that e.g. A/C won't support that :).
IOPSGetProvidingPowerSourceType() like the Windows API will tell you if you're on A/C (call this "infinite"), D/C (long term battery), or UPS (emergency).
IOPSGetTimeRemainingEstimate() will return a battery time estimate. This again supports my argument that the grown up platforms will offer a time remaining and would much rather let users and applications deal in that than a percentage which is absolutely useless to everyone (who would then just have to calculate this anyway to do anything remotely useful).
Anssi: of note, battery percentages can vary wildly if someone is amusing and swaps battery sources, a system can easily go from 100% charged to 50% charged (insert an empty battery), or from 99% charged to 33% charged (insert an empty high capacity battery). For more fun, it could go from 40% charged (kinda low), to 15% charged (insert an empty high capacity battery while trying to give more power to the system). In each of these cases, the actual amount of time remaining for the system hasn't changed, but you're going to cause any application listening to a percentage to panic.
Next stop: Android
The Android BatteryManager [7]. I think that for Android one could get a rough estimate of time remaining by adapting the stack overflow sample [8]: Get the current voltage, sleep time X, get the new voltage, calculate the burn rate, estimate how long it would take to burn the remaining voltage.
TimeRemaining = VoltageNow * (VoltageOrginal - VoltageNow) / (TimeNow - TimeOriginal)
For most platforms one can get a rough estimate while the device is on battery. Sure one can't necessarily get a decent answer on some platforms while charging, but if the Host was alive before A/C was added, one can cache that answer and say "I think I have that + 5 minutes, and oh, I'm on A/C".
In any case, if one has a listener who says "Tell me if the system will run out of power before {Date.now() + 5 minutes}", then the host can calculate the most demanding listener (this list is sortable, sort by Date and take the biggest one), and consider its current estimate for time remaining. It can choose to sleep battery polling for a few minutes if at its last sampling, things seemed to be ok for a couple of minutes. It would only need to do work when it gets a signal that battery remaining has dropped sufficient that it would endanger the task which expects to take the longest to complete. The system can also fire an event when that Task's completion time happens saying "Hi, your request for cpu cycles has expired, if you need more guaranteed cycles, you will need to make a new reservation request".
dictionary TaskReservationStatus { /* "TaskReserved" fired at most once, indicating that the host believes the task can complete before the system stops (runs out of power, is turned off by user, is turned off due to a schedule */ /* "TaskExpired" fired when TaskReservation.estimatedCompletionTime >= Date() */ /* "BatteryWarning" fired if !onACPower() && (TaskReservation.estimatedCompletionTime >= Date() + EstimatedBatteryRemaining()) */ DOMString type; /* when the event was fired */ Date timeStamp; /* For "BatteryWarning" this will be a Date hinting about when the system will stop running instructions. For TaskReserved and TaskExpired, this may be null or a time > TaskReservation.estimatedCompletionTime */ Date? estimatedLastCycle; /* This is TaskReservation.description for debugging purposes - As TaskReservation will expire if it's GCd, there is no reference to it in this Object */ DOMString? description; }; [Callback, Callable, NoInterfaceObject] interface TaskReservationCallback { function void handleEvent(TaskReservationStatus status); } [NamedConstructor= /* create a reservation indicating when you expect your task to complete @arg estimatedCompletionTime - when the Task should be finished @arg reservationWarning - called with a couple of events: "TaskExpired" - when this.estimatedCompletionTime >= Date (), at which point this TaskReservation will cease firing events "BatteryWarning" - called if the system determines that it is unlikely to be able to reach this.estimatedCompletionTime before running out of power @arg description - provided so that a UA can tell the user why it thinks it needs to be running until a given time - this can be reflected in SystemRequestsToShutdown (e.g. triggered by user doing start>shutdown), or if the user wants to know why the application is burning cpu cycles - UAs can choose to encourage users to terminate tasks which do not have an understandable description (and certainly anything which doesn't have an active TaskReservation) */ TaskReservation(Date estimatedCompletionTime, TaskReservationCallback reservationWarning, optional DOMString description) ] interface TaskReservation { /* the current task end time, this is read write, because the application which created the task can decide that it needs more or less time - setting this.estimatedCompletionTime to any value <= Date.now() will cause the Task to expire If the UA GCs the TaskReservation, then it will also expire... */ attribute Date estimatedCompletionTime; /* this is a string describing the Task, if you need a new description, then you should create a new Task, and expire the old one. */ readonly DOMString? description; };
I believe that this interface covers most *real* use cases beyond a battery monitor (which is a really stupid application, and which given the current API does not handle fun cases like UPS, multiple batteries, different charging sources - all of which are critical to a battery monitor app).
I should provide a js impl for this....