* lisp/emacs-lisp/bytecomp.el: Silence obsolete warnings more reliably.
[emacs.git] / lisp / battery.el
blob3b245ed644d80e0abe0801530912b93512d811b9
1 ;;; battery.el --- display battery status information -*- coding: iso-8859-1 -*-
3 ;; Copyright (C) 1997-1998, 2000-2011 Free Software Foundation, Inc.
5 ;; Author: Ralph Schleicher <rs@nunatak.allgaeu.org>
6 ;; Keywords: hardware
8 ;; This file is part of GNU Emacs.
10 ;; GNU Emacs is free software: you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation, either version 3 of the License, or
13 ;; (at your option) any later version.
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
23 ;;; Commentary:
25 ;; There is at present support for GNU/Linux, OS X and Windows. This
26 ;; library supports both the `/proc/apm' file format of Linux version
27 ;; 1.3.58 or newer and the `/proc/acpi/' directory structure of Linux
28 ;; 2.4.20 and 2.6. Darwin (OS X) is supported by using the `pmset'
29 ;; program. Windows is supported by the GetSystemPowerStatus API call.
31 ;;; Code:
33 (require 'timer)
34 (eval-when-compile (require 'cl))
37 (defgroup battery nil
38 "Display battery status information."
39 :prefix "battery-"
40 :group 'hardware)
42 (defcustom battery-status-function
43 (cond ((and (eq system-type 'gnu/linux)
44 (file-readable-p "/proc/apm"))
45 'battery-linux-proc-apm)
46 ((and (eq system-type 'gnu/linux)
47 (file-directory-p "/proc/acpi/battery"))
48 'battery-linux-proc-acpi)
49 ((and (eq system-type 'gnu/linux)
50 (file-directory-p "/sys/class/power_supply/")
51 (directory-files "/sys/class/power_supply/" nil "BAT[0-9]$"))
52 'battery-linux-sysfs)
53 ((and (eq system-type 'darwin)
54 (condition-case nil
55 (with-temp-buffer
56 (and (eq (call-process "pmset" nil t nil "-g" "ps") 0)
57 (> (buffer-size) 0)))
58 (error nil)))
59 'battery-pmset)
60 ((eq system-type 'windows-nt)
61 'w32-battery-status))
62 "Function for getting battery status information.
63 The function has to return an alist of conversion definitions.
64 Its cons cells are of the form
66 (CONVERSION . REPLACEMENT-TEXT)
68 CONVERSION is the character code of a \"conversion specification\"
69 introduced by a `%' character in a control string."
70 :type '(choice (const nil) function)
71 :group 'battery)
73 (defcustom battery-echo-area-format
74 (cond ((eq battery-status-function 'battery-linux-proc-acpi)
75 "Power %L, battery %B at %r (%p%% load, remaining time %t)")
76 ((eq battery-status-function 'battery-linux-sysfs)
77 "Power %L, battery %B (%p%% load)")
78 ((eq battery-status-function 'battery-pmset)
79 "%L power, battery %B (%p%% load, remaining time %t)")
80 (battery-status-function
81 "Power %L, battery %B (%p%% load, remaining time %t)"))
82 "Control string formatting the string to display in the echo area.
83 Ordinary characters in the control string are printed as-is, while
84 conversion specifications introduced by a `%' character in the control
85 string are substituted as defined by the current value of the variable
86 `battery-status-function'. Here are the ones generally available:
87 %c Current capacity (mAh or mWh)
88 %r Current rate of charge or discharge
89 %B Battery status (verbose)
90 %b Battery status: empty means high, `-' means low,
91 `!' means critical, and `+' means charging
92 %d Temperature (in degrees Celsius)
93 %L AC line status (verbose)
94 %p Battery load percentage
95 %m Remaining time (to charge or discharge) in minutes
96 %h Remaining time (to charge or discharge) in hours
97 %t Remaining time (to charge or discharge) in the form `h:min'"
98 :type '(choice string (const nil))
99 :group 'battery)
101 (defvar battery-mode-line-string nil
102 "String to display in the mode line.")
103 ;;;###autoload (put 'battery-mode-line-string 'risky-local-variable t)
105 (defcustom battery-mode-line-limit 100
106 "Percentage of full battery load below which display battery status"
107 :type 'integer
108 :group 'battery)
110 (defcustom battery-mode-line-format
111 (cond ((eq battery-status-function 'battery-linux-proc-acpi)
112 "[%b%p%%,%d°C]")
113 (battery-status-function
114 "[%b%p%%]"))
115 "Control string formatting the string to display in the mode line.
116 Ordinary characters in the control string are printed as-is, while
117 conversion specifications introduced by a `%' character in the control
118 string are substituted as defined by the current value of the variable
119 `battery-status-function'. Here are the ones generally available:
120 %c Current capacity (mAh or mWh)
121 %r Current rate of charge or discharge
122 %B Battery status (verbose)
123 %b Battery status: empty means high, `-' means low,
124 `!' means critical, and `+' means charging
125 %d Temperature (in degrees Celsius)
126 %L AC line status (verbose)
127 %p Battery load percentage
128 %m Remaining time (to charge or discharge) in minutes
129 %h Remaining time (to charge or discharge) in hours
130 %t Remaining time (to charge or discharge) in the form `h:min'"
131 :type '(choice string (const nil))
132 :group 'battery)
134 (defcustom battery-update-interval 60
135 "Seconds after which the battery status will be updated."
136 :type 'integer
137 :group 'battery)
139 (defcustom battery-load-low 25
140 "Upper bound of low battery load percentage.
141 A battery load percentage below this number is considered low."
142 :type 'integer
143 :group 'battery)
145 (defcustom battery-load-critical 10
146 "Upper bound of critical battery load percentage.
147 A battery load percentage below this number is considered critical."
148 :type 'integer
149 :group 'battery)
151 (defvar battery-update-timer nil
152 "Interval timer object.")
154 ;;;###autoload
155 (defun battery ()
156 "Display battery status information in the echo area.
157 The text being displayed in the echo area is controlled by the variables
158 `battery-echo-area-format' and `battery-status-function'."
159 (interactive)
160 (message "%s" (if (and battery-echo-area-format battery-status-function)
161 (battery-format battery-echo-area-format
162 (funcall battery-status-function))
163 "Battery status not available")))
165 ;;;###autoload
166 (define-minor-mode display-battery-mode
167 "Toggle battery status display in mode line (Display Battery mode).
168 With a prefix argument ARG, enable Display Battery mode if ARG is
169 positive, and disable it otherwise. If called from Lisp, enable
170 the mode if ARG is omitted or nil.
172 The text displayed in the mode line is controlled by
173 `battery-mode-line-format' and `battery-status-function'.
174 The mode line is be updated every `battery-update-interval'
175 seconds."
176 :global t :group 'battery
177 (setq battery-mode-line-string "")
178 (or global-mode-string (setq global-mode-string '("")))
179 (and battery-update-timer (cancel-timer battery-update-timer))
180 (if (and battery-status-function battery-mode-line-format)
181 (if (not display-battery-mode)
182 (setq global-mode-string
183 (delq 'battery-mode-line-string global-mode-string))
184 (add-to-list 'global-mode-string 'battery-mode-line-string t)
185 (setq battery-update-timer (run-at-time nil battery-update-interval
186 'battery-update-handler))
187 (battery-update))
188 (message "Battery status not available")
189 (setq display-battery-mode nil)))
191 (defun battery-update-handler ()
192 (battery-update)
193 (sit-for 0))
195 (defun battery-update ()
196 "Update battery status information in the mode line."
197 (let ((data (and battery-status-function (funcall battery-status-function))))
198 (setq battery-mode-line-string
199 (propertize (if (and battery-mode-line-format
200 (<= (car (read-from-string (cdr (assq ?p data))))
201 battery-mode-line-limit))
202 (battery-format
203 battery-mode-line-format
204 data)
206 'face
207 (and (<= (car (read-from-string (cdr (assq ?p data))))
208 battery-load-critical)
209 'error)
210 'help-echo "Battery status information")))
211 (force-mode-line-update))
213 ;;; `/proc/apm' interface for Linux.
215 (defconst battery-linux-proc-apm-regexp
216 (concat "^\\([^ ]+\\)" ; Driver version.
217 " \\([^ ]+\\)" ; APM BIOS version.
218 " 0x\\([0-9a-f]+\\)" ; APM BIOS flags.
219 " 0x\\([0-9a-f]+\\)" ; AC line status.
220 " 0x\\([0-9a-f]+\\)" ; Battery status.
221 " 0x\\([0-9a-f]+\\)" ; Battery flags.
222 " \\(-?[0-9]+\\)%" ; Load percentage.
223 " \\(-?[0-9]+\\)" ; Remaining time.
224 " \\(.*\\)" ; Time unit.
225 "$")
226 "Regular expression matching contents of `/proc/apm'.")
228 (defun battery-linux-proc-apm ()
229 "Get APM status information from Linux kernel.
230 This function works only with the new `/proc/apm' format introduced
231 in Linux version 1.3.58.
233 The following %-sequences are provided:
234 %v Linux driver version
235 %V APM BIOS version
236 %I APM BIOS status (verbose)
237 %L AC line status (verbose)
238 %B Battery status (verbose)
239 %b Battery status, empty means high, `-' means low,
240 `!' means critical, and `+' means charging
241 %p Battery load percentage
242 %s Remaining time (to charge or discharge) in seconds
243 %m Remaining time (to charge or discharge) in minutes
244 %h Remaining time (to charge or discharge) in hours
245 %t Remaining time (to charge or discharge) in the form `h:min'"
246 (let (driver-version bios-version bios-interface line-status
247 battery-status battery-status-symbol load-percentage
248 seconds minutes hours remaining-time tem)
249 (with-temp-buffer
250 (ignore-errors (insert-file-contents "/proc/apm"))
251 (when (re-search-forward battery-linux-proc-apm-regexp)
252 (setq driver-version (match-string 1))
253 (setq bios-version (match-string 2))
254 (setq tem (string-to-number (match-string 3) 16))
255 (if (not (logand tem 2))
256 (setq bios-interface "not supported")
257 (setq bios-interface "enabled")
258 (cond ((logand tem 16) (setq bios-interface "disabled"))
259 ((logand tem 32) (setq bios-interface "disengaged")))
260 (setq tem (string-to-number (match-string 4) 16))
261 (cond ((= tem 0) (setq line-status "off-line"))
262 ((= tem 1) (setq line-status "on-line"))
263 ((= tem 2) (setq line-status "on backup")))
264 (setq tem (string-to-number (match-string 6) 16))
265 (if (= tem 255)
266 (setq battery-status "N/A")
267 (setq tem (string-to-number (match-string 5) 16))
268 (cond ((= tem 0) (setq battery-status "high"
269 battery-status-symbol ""))
270 ((= tem 1) (setq battery-status "low"
271 battery-status-symbol "-"))
272 ((= tem 2) (setq battery-status "critical"
273 battery-status-symbol "!"))
274 ((= tem 3) (setq battery-status "charging"
275 battery-status-symbol "+")))
276 (setq load-percentage (match-string 7))
277 (setq seconds (string-to-number (match-string 8)))
278 (and (string-equal (match-string 9) "min")
279 (setq seconds (* 60 seconds)))
280 (setq minutes (/ seconds 60)
281 hours (/ seconds 3600))
282 (setq remaining-time
283 (format "%d:%02d" hours (- minutes (* 60 hours))))))))
284 (list (cons ?v (or driver-version "N/A"))
285 (cons ?V (or bios-version "N/A"))
286 (cons ?I (or bios-interface "N/A"))
287 (cons ?L (or line-status "N/A"))
288 (cons ?B (or battery-status "N/A"))
289 (cons ?b (or battery-status-symbol ""))
290 (cons ?p (or load-percentage "N/A"))
291 (cons ?s (or (and seconds (number-to-string seconds)) "N/A"))
292 (cons ?m (or (and minutes (number-to-string minutes)) "N/A"))
293 (cons ?h (or (and hours (number-to-string hours)) "N/A"))
294 (cons ?t (or remaining-time "N/A")))))
297 ;;; `/proc/acpi/' interface for Linux.
299 (defun battery-linux-proc-acpi ()
300 "Get ACPI status information from Linux kernel.
301 This function works only with the `/proc/acpi/' format introduced
302 in Linux version 2.4.20 and 2.6.0.
304 The following %-sequences are provided:
305 %c Current capacity (mAh)
306 %r Current rate
307 %B Battery status (verbose)
308 %b Battery status, empty means high, `-' means low,
309 `!' means critical, and `+' means charging
310 %d Temperature (in degrees Celsius)
311 %L AC line status (verbose)
312 %p Battery load percentage
313 %m Remaining time (to charge or discharge) in minutes
314 %h Remaining time (to charge or discharge) in hours
315 %t Remaining time (to charge or discharge) in the form `h:min'"
316 (let ((design-capacity 0)
317 (last-full-capacity 0)
318 full-capacity
319 (warn 0)
320 (low 0)
321 capacity rate rate-type charging-state minutes hours)
322 ;; ACPI provides information about each battery present in the system in
323 ;; a separate subdirectory. We are going to merge the available
324 ;; information together since displaying for a variable amount of
325 ;; batteries seems overkill for format-strings.
326 (with-temp-buffer
327 (dolist (dir (ignore-errors (directory-files "/proc/acpi/battery/"
328 t "\\`[^.]")))
329 (erase-buffer)
330 (ignore-errors (insert-file-contents (expand-file-name "state" dir)))
331 (when (re-search-forward "present: +yes$" nil t)
332 (and (re-search-forward "charging state: +\\(.*\\)$" nil t)
333 (member charging-state '("unknown" "charged" nil))
334 ;; On most multi-battery systems, most of the time only one
335 ;; battery is "charging"/"discharging", the others are
336 ;; "unknown".
337 (setq charging-state (match-string 1)))
338 (when (re-search-forward "present rate: +\\([0-9]+\\) \\(m[AW]\\)$"
339 nil t)
340 (setq rate (+ (or rate 0) (string-to-number (match-string 1)))
341 rate-type (or (and rate-type
342 (if (string= rate-type (match-string 2))
343 rate-type
344 (error
345 "Inconsistent rate types (%s vs. %s)"
346 rate-type (match-string 2))))
347 (match-string 2))))
348 (when (re-search-forward "remaining capacity: +\\([0-9]+\\) m[AW]h$"
349 nil t)
350 (setq capacity
351 (+ (or capacity 0) (string-to-number (match-string 1))))))
352 (goto-char (point-max))
353 (ignore-errors (insert-file-contents (expand-file-name "info" dir)))
354 (when (re-search-forward "present: +yes$" nil t)
355 (when (re-search-forward "design capacity: +\\([0-9]+\\) m[AW]h$"
356 nil t)
357 (incf design-capacity (string-to-number (match-string 1))))
358 (when (re-search-forward "last full capacity: +\\([0-9]+\\) m[AW]h$"
359 nil t)
360 (incf last-full-capacity (string-to-number (match-string 1))))
361 (when (re-search-forward
362 "design capacity warning: +\\([0-9]+\\) m[AW]h$" nil t)
363 (incf warn (string-to-number (match-string 1))))
364 (when (re-search-forward "design capacity low: +\\([0-9]+\\) m[AW]h$"
365 nil t)
366 (incf low (string-to-number (match-string 1)))))))
367 (setq full-capacity (if (> last-full-capacity 0)
368 last-full-capacity design-capacity))
369 (and capacity rate
370 (setq minutes (if (zerop rate) 0
371 (floor (* (/ (float (if (string= charging-state
372 "charging")
373 (- full-capacity capacity)
374 capacity))
375 rate)
376 60)))
377 hours (/ minutes 60)))
378 (list (cons ?c (or (and capacity (number-to-string capacity)) "N/A"))
379 (cons ?L (or (battery-search-for-one-match-in-files
380 (mapcar (lambda (e) (concat e "/state"))
381 (ignore-errors
382 (directory-files "/proc/acpi/ac_adapter/"
383 t "\\`[^.]")))
384 "state: +\\(.*\\)$" 1)
386 "N/A"))
387 (cons ?d (or (battery-search-for-one-match-in-files
388 (mapcar (lambda (e) (concat e "/temperature"))
389 (ignore-errors
390 (directory-files "/proc/acpi/thermal_zone/"
391 t "\\`[^.]")))
392 "temperature: +\\([0-9]+\\) C$" 1)
394 "N/A"))
395 (cons ?r (or (and rate (concat (number-to-string rate) " "
396 rate-type)) "N/A"))
397 (cons ?B (or charging-state "N/A"))
398 (cons ?b (or (and (string= charging-state "charging") "+")
399 (and capacity (< capacity low) "!")
400 (and capacity (< capacity warn) "-")
401 ""))
402 (cons ?h (or (and hours (number-to-string hours)) "N/A"))
403 (cons ?m (or (and minutes (number-to-string minutes)) "N/A"))
404 (cons ?t (or (and minutes
405 (format "%d:%02d" hours (- minutes (* 60 hours))))
406 "N/A"))
407 (cons ?p (or (and full-capacity capacity
408 (> full-capacity 0)
409 (number-to-string
410 (floor (/ capacity
411 (/ (float full-capacity) 100)))))
412 "N/A")))))
415 ;;; `/sys/class/power_supply/BATN' interface for Linux.
417 (defun battery-linux-sysfs ()
418 "Get ACPI status information from Linux kernel.
419 This function works only with the new `/sys/class/power_supply/'
420 format introduced in Linux version 2.4.25.
422 The following %-sequences are provided:
423 %c Current capacity (mAh or mWh)
424 %B Battery status (verbose)
425 %p Battery load percentage
426 %L AC line status (verbose)"
427 (let (charging-state
428 (charge-full 0.0)
429 (charge-now 0.0)
430 (energy-full 0.0)
431 (energy-now 0.0))
432 ;; SysFS provides information about each battery present in the
433 ;; system in a separate subdirectory. We are going to merge the
434 ;; available information together.
435 (with-temp-buffer
436 (dolist (dir (ignore-errors
437 (directory-files
438 "/sys/class/power_supply/" t "BAT[0-9]$")))
439 (erase-buffer)
440 (ignore-errors (insert-file-contents
441 (expand-file-name "uevent" dir)))
442 (when (re-search-forward "POWER_SUPPLY_PRESENT=1$" nil t)
443 (goto-char (point-min))
444 (and (re-search-forward "POWER_SUPPLY_STATUS=\\(.*\\)$" nil t)
445 (member charging-state '("Unknown" "Full" nil))
446 (setq charging-state (match-string 1)))
447 (let (full-string now-string)
448 ;; Sysfs may list either charge (mAh) or energy (mWh).
449 ;; Keep track of both, and choose which to report later.
450 (cond ((and (re-search-forward
451 "POWER_SUPPLY_CHARGE_FULL=\\([0-9]*\\)$" nil t)
452 (setq full-string (match-string 1))
453 (re-search-forward
454 "POWER_SUPPLY_CHARGE_NOW=\\([0-9]*\\)$" nil t)
455 (setq now-string (match-string 1)))
456 (setq charge-full (+ charge-full
457 (string-to-number full-string))
458 charge-now (+ charge-now
459 (string-to-number now-string))))
460 ((and (re-search-forward
461 "POWER_SUPPLY_ENERGY_FULL=\\([0-9]*\\)$" nil t)
462 (setq full-string (match-string 1))
463 (re-search-forward
464 "POWER_SUPPLY_ENERGY_NOW=\\([0-9]*\\)$" nil t)
465 (setq now-string (match-string 1)))
466 (setq energy-full (+ energy-full
467 (string-to-number full-string))
468 energy-now (+ energy-now
469 (string-to-number now-string)))))))))
470 (list (cons ?c (cond ((or (> charge-full 0) (> charge-now 0))
471 (number-to-string charge-now))
472 ((or (> energy-full 0) (> energy-now 0))
473 (number-to-string energy-now))
474 (t "N/A")))
475 (cons ?B (or charging-state "N/A"))
476 (cons ?p (cond ((> charge-full 0)
477 (format "%.1f"
478 (/ (* 100 charge-now) charge-full)))
479 ((> energy-full 0)
480 (format "%.1f"
481 (/ (* 100 energy-now) energy-full)))
482 (t "N/A")))
483 (cons ?L (if (file-readable-p "/sys/class/power_supply/AC/online")
484 (if (battery-search-for-one-match-in-files
485 (list "/sys/class/power_supply/AC/online"
486 "/sys/class/power_supply/ACAD/online")
487 "1" 0)
488 "AC"
489 "BAT")
490 "N/A")))))
494 ;;; `pmset' interface for Darwin (OS X).
496 (defun battery-pmset ()
497 "Get battery status information using `pmset'.
499 The following %-sequences are provided:
500 %L Power source (verbose)
501 %B Battery status (verbose)
502 %b Battery status, empty means high, `-' means low,
503 `!' means critical, and `+' means charging
504 %p Battery load percentage
505 %h Remaining time in hours
506 %m Remaining time in minutes
507 %t Remaining time in the form `h:min'"
508 (let (power-source load-percentage battery-status battery-status-symbol
509 remaining-time hours minutes)
510 (with-temp-buffer
511 (ignore-errors (call-process "pmset" nil t nil "-g" "ps"))
512 (goto-char (point-min))
513 (when (re-search-forward "Currentl?y drawing from '\\(AC\\|Battery\\) Power'" nil t)
514 (setq power-source (match-string 1))
515 (when (re-search-forward "^ -InternalBattery-0[ \t]+" nil t)
516 (when (looking-at "\\([0-9]\\{1,3\\}\\)%")
517 (setq load-percentage (match-string 1))
518 (goto-char (match-end 0))
519 (cond ((looking-at "; charging")
520 (setq battery-status "charging"
521 battery-status-symbol "+"))
522 ((< (string-to-number load-percentage) battery-load-low)
523 (setq battery-status "low"
524 battery-status-symbol "-"))
525 ((< (string-to-number load-percentage) battery-load-critical)
526 (setq battery-status "critical"
527 battery-status-symbol "!"))
529 (setq battery-status "high"
530 battery-status-symbol "")))
531 (when (re-search-forward "\\(\\([0-9]+\\):\\([0-9]+\\)\\) remaining" nil t)
532 (setq remaining-time (match-string 1))
533 (let ((h (string-to-number (match-string 2)))
534 (m (string-to-number (match-string 3))))
535 (setq hours (number-to-string (+ h (if (< m 30) 0 1)))
536 minutes (number-to-string (+ (* h 60) m)))))))))
537 (list (cons ?L (or power-source "N/A"))
538 (cons ?p (or load-percentage "N/A"))
539 (cons ?B (or battery-status "N/A"))
540 (cons ?b (or battery-status-symbol ""))
541 (cons ?h (or hours "N/A"))
542 (cons ?m (or minutes "N/A"))
543 (cons ?t (or remaining-time "N/A")))))
546 ;;; Private functions.
548 (defun battery-format (format alist)
549 "Substitute %-sequences in FORMAT."
550 (replace-regexp-in-string
551 "%."
552 (lambda (str)
553 (let ((char (aref str 1)))
554 (if (eq char ?%) "%"
555 (or (cdr (assoc char alist)) ""))))
556 format t t))
558 (defun battery-search-for-one-match-in-files (files regexp match-num)
559 "Search REGEXP in the content of the files listed in FILES.
560 If a match occurred, return the parenthesized expression numbered by
561 MATCH-NUM in the match. Otherwise, return nil."
562 (with-temp-buffer
563 (catch 'found
564 (dolist (file files)
565 (and (ignore-errors (insert-file-contents file nil nil nil 'replace))
566 (re-search-forward regexp nil t)
567 (throw 'found (match-string match-num)))))))
570 (provide 'battery)
572 ;;; battery.el ends here