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