battery.el 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758
  1. ;;; battery.el --- display battery status information
  2. ;; Copyright (C) 1997-1998, 2000-2017 Free Software Foundation, Inc.
  3. ;; Author: Ralph Schleicher <rs@nunatak.allgaeu.org>
  4. ;; Keywords: hardware
  5. ;; This file is part of GNU Emacs.
  6. ;; GNU Emacs is free software: you can redistribute it and/or modify
  7. ;; it under the terms of the GNU General Public License as published by
  8. ;; the Free Software Foundation, either version 3 of the License, or
  9. ;; (at your option) any later version.
  10. ;; GNU Emacs is distributed in the hope that it will be useful,
  11. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ;; GNU General Public License for more details.
  14. ;; You should have received a copy of the GNU General Public License
  15. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  16. ;;; Commentary:
  17. ;; There is at present support for GNU/Linux, macOS and Windows. This
  18. ;; library supports both the `/proc/apm' file format of Linux version
  19. ;; 1.3.58 or newer and the `/proc/acpi/' directory structure of Linux
  20. ;; 2.4.20 and 2.6. Darwin (macOS) is supported by using the `pmset'
  21. ;; program. Windows is supported by the GetSystemPowerStatus API call.
  22. ;;; Code:
  23. (require 'timer)
  24. (eval-when-compile (require 'cl-lib))
  25. (defgroup battery nil
  26. "Display battery status information."
  27. :prefix "battery-"
  28. :group 'hardware)
  29. (defcustom battery-linux-sysfs-regexp "[bB][aA][tT][0-9]?$"
  30. "Regexp for folder names to be searched under
  31. /sys/class/power_supply/ that contain battery information."
  32. :version "26.1"
  33. :type 'regexp
  34. :group 'battery)
  35. (defcustom battery-upower-device "battery_BAT1"
  36. "Upower battery device name."
  37. :version "26.1"
  38. :type 'string
  39. :group 'battery)
  40. (defcustom battery-status-function
  41. (cond ((and (eq system-type 'gnu/linux)
  42. (file-readable-p "/proc/apm"))
  43. #'battery-linux-proc-apm)
  44. ((and (eq system-type 'gnu/linux)
  45. (file-directory-p "/proc/acpi/battery"))
  46. #'battery-linux-proc-acpi)
  47. ((and (eq system-type 'gnu/linux)
  48. (file-directory-p "/sys/class/power_supply/")
  49. (directory-files "/sys/class/power_supply/" nil
  50. battery-linux-sysfs-regexp))
  51. #'battery-linux-sysfs)
  52. ((and (eq system-type 'berkeley-unix)
  53. (file-executable-p "/usr/sbin/apm"))
  54. #'battery-bsd-apm)
  55. ((and (eq system-type 'darwin)
  56. (condition-case nil
  57. (with-temp-buffer
  58. (and (eq (call-process "pmset" nil t nil "-g" "ps") 0)
  59. (> (buffer-size) 0)))
  60. (error nil)))
  61. #'battery-pmset)
  62. ((fboundp 'w32-battery-status)
  63. #'w32-battery-status))
  64. "Function for getting battery status information.
  65. The function has to return an alist of conversion definitions.
  66. Its cons cells are of the form
  67. (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)
  72. (defcustom battery-echo-area-format
  73. "Power %L, battery %B (%p%% load, remaining time %t)"
  74. "Control string formatting the string to display in the echo area.
  75. Ordinary characters in the control string are printed as-is, while
  76. conversion specifications introduced by a `%' character in the control
  77. string are substituted as defined by the current value of the variable
  78. `battery-status-function'. Here are the ones generally available:
  79. %c Current capacity (mAh or mWh)
  80. %r Current rate of charge or discharge
  81. %B Battery status (verbose)
  82. %b Battery status: empty means high, `-' means low,
  83. `!' means critical, and `+' means charging
  84. %d Temperature (in degrees Celsius)
  85. %L AC line status (verbose)
  86. %p Battery load percentage
  87. %m Remaining time (to charge or discharge) in minutes
  88. %h Remaining time (to charge or discharge) in hours
  89. %t Remaining time (to charge or discharge) in the form `h:min'"
  90. :type '(choice string (const nil))
  91. :group 'battery)
  92. (defvar battery-mode-line-string nil
  93. "String to display in the mode line.")
  94. ;;;###autoload (put 'battery-mode-line-string 'risky-local-variable t)
  95. (defcustom battery-mode-line-limit 100
  96. "Percentage of full battery load below which display battery status"
  97. :version "24.1"
  98. :type 'integer
  99. :group 'battery)
  100. (defcustom battery-mode-line-format
  101. (cond ((eq battery-status-function 'battery-linux-proc-acpi)
  102. "[%b%p%%,%d°C]")
  103. (battery-status-function
  104. "[%b%p%%]"))
  105. "Control string formatting the string to display in the mode line.
  106. Ordinary characters in the control string are printed as-is, while
  107. conversion specifications introduced by a `%' character in the control
  108. string are substituted as defined by the current value of the variable
  109. `battery-status-function'. Here are the ones generally available:
  110. %c Current capacity (mAh or mWh)
  111. %r Current rate of charge or discharge
  112. %B Battery status (verbose)
  113. %b Battery status: empty means high, `-' means low,
  114. `!' means critical, and `+' means charging
  115. %d Temperature (in degrees Celsius)
  116. %L AC line status (verbose)
  117. %p Battery load percentage
  118. %m Remaining time (to charge or discharge) in minutes
  119. %h Remaining time (to charge or discharge) in hours
  120. %t Remaining time (to charge or discharge) in the form `h:min'"
  121. :type '(choice string (const nil))
  122. :group 'battery)
  123. (defcustom battery-update-interval 60
  124. "Seconds after which the battery status will be updated."
  125. :type 'integer
  126. :group 'battery)
  127. (defcustom battery-load-low 25
  128. "Upper bound of low battery load percentage.
  129. A battery load percentage below this number is considered low."
  130. :type 'integer
  131. :group 'battery)
  132. (defcustom battery-load-critical 10
  133. "Upper bound of critical battery load percentage.
  134. A battery load percentage below this number is considered critical."
  135. :type 'integer
  136. :group 'battery)
  137. (defvar battery-update-timer nil
  138. "Interval timer object.")
  139. ;;;###autoload
  140. (defun battery ()
  141. "Display battery status information in the echo area.
  142. The text being displayed in the echo area is controlled by the variables
  143. `battery-echo-area-format' and `battery-status-function'."
  144. (interactive)
  145. (message "%s" (if (and battery-echo-area-format battery-status-function)
  146. (battery-format battery-echo-area-format
  147. (funcall battery-status-function))
  148. "Battery status not available")))
  149. ;;;###autoload
  150. (define-minor-mode display-battery-mode
  151. "Toggle battery status display in mode line (Display Battery mode).
  152. With a prefix argument ARG, enable Display Battery mode if ARG is
  153. positive, and disable it otherwise. If called from Lisp, enable
  154. the mode if ARG is omitted or nil.
  155. The text displayed in the mode line is controlled by
  156. `battery-mode-line-format' and `battery-status-function'.
  157. The mode line is be updated every `battery-update-interval'
  158. seconds."
  159. :global t :group 'battery
  160. (setq battery-mode-line-string "")
  161. (or global-mode-string (setq global-mode-string '("")))
  162. (and battery-update-timer (cancel-timer battery-update-timer))
  163. (if (and battery-status-function battery-mode-line-format)
  164. (if (not display-battery-mode)
  165. (setq global-mode-string
  166. (delq 'battery-mode-line-string global-mode-string))
  167. (add-to-list 'global-mode-string 'battery-mode-line-string t)
  168. (setq battery-update-timer (run-at-time nil battery-update-interval
  169. 'battery-update-handler))
  170. (battery-update))
  171. (message "Battery status not available")
  172. (setq display-battery-mode nil)))
  173. (defun battery-update-handler ()
  174. (battery-update)
  175. (sit-for 0))
  176. (defun battery-update ()
  177. "Update battery status information in the mode line."
  178. (let* ((data (and battery-status-function (funcall battery-status-function)))
  179. (percentage (car (read-from-string (cdr (assq ?p data))))))
  180. (setq battery-mode-line-string
  181. (propertize (if (and battery-mode-line-format
  182. (numberp percentage)
  183. (<= percentage battery-mode-line-limit))
  184. (battery-format battery-mode-line-format data)
  185. "")
  186. 'face
  187. (and (numberp percentage)
  188. (<= percentage battery-load-critical)
  189. 'error)
  190. 'help-echo "Battery status information")))
  191. (force-mode-line-update))
  192. ;;; `/proc/apm' interface for Linux.
  193. (defconst battery-linux-proc-apm-regexp
  194. (concat "^\\([^ ]+\\)" ; Driver version.
  195. " \\([^ ]+\\)" ; APM BIOS version.
  196. " 0x\\([0-9a-f]+\\)" ; APM BIOS flags.
  197. " 0x\\([0-9a-f]+\\)" ; AC line status.
  198. " 0x\\([0-9a-f]+\\)" ; Battery status.
  199. " 0x\\([0-9a-f]+\\)" ; Battery flags.
  200. " \\(-?[0-9]+\\)%" ; Load percentage.
  201. " \\(-?[0-9]+\\)" ; Remaining time.
  202. " \\(.*\\)" ; Time unit.
  203. "$")
  204. "Regular expression matching contents of `/proc/apm'.")
  205. (defun battery-linux-proc-apm ()
  206. "Get APM status information from Linux (the kernel).
  207. This function works only with the new `/proc/apm' format introduced
  208. in Linux version 1.3.58.
  209. The following %-sequences are provided:
  210. %v Linux driver version
  211. %V APM BIOS version
  212. %I APM BIOS status (verbose)
  213. %L AC line status (verbose)
  214. %B Battery status (verbose)
  215. %b Battery status, empty means high, `-' means low,
  216. `!' means critical, and `+' means charging
  217. %p Battery load percentage
  218. %s Remaining time (to charge or discharge) in seconds
  219. %m Remaining time (to charge or discharge) in minutes
  220. %h Remaining time (to charge or discharge) in hours
  221. %t Remaining time (to charge or discharge) in the form `h:min'"
  222. (let (driver-version bios-version bios-interface line-status
  223. battery-status battery-status-symbol load-percentage
  224. seconds minutes hours remaining-time tem)
  225. (with-temp-buffer
  226. (ignore-errors (insert-file-contents "/proc/apm"))
  227. (when (re-search-forward battery-linux-proc-apm-regexp)
  228. (setq driver-version (match-string 1))
  229. (setq bios-version (match-string 2))
  230. (setq tem (string-to-number (match-string 3) 16))
  231. (if (not (logand tem 2))
  232. (setq bios-interface "not supported")
  233. (setq bios-interface "enabled")
  234. (cond ((logand tem 16) (setq bios-interface "disabled"))
  235. ((logand tem 32) (setq bios-interface "disengaged")))
  236. (setq tem (string-to-number (match-string 4) 16))
  237. (cond ((= tem 0) (setq line-status "off-line"))
  238. ((= tem 1) (setq line-status "on-line"))
  239. ((= tem 2) (setq line-status "on backup")))
  240. (setq tem (string-to-number (match-string 6) 16))
  241. (if (= tem 255)
  242. (setq battery-status "N/A")
  243. (setq tem (string-to-number (match-string 5) 16))
  244. (cond ((= tem 0) (setq battery-status "high"
  245. battery-status-symbol ""))
  246. ((= tem 1) (setq battery-status "low"
  247. battery-status-symbol "-"))
  248. ((= tem 2) (setq battery-status "critical"
  249. battery-status-symbol "!"))
  250. ((= tem 3) (setq battery-status "charging"
  251. battery-status-symbol "+")))
  252. (setq load-percentage (match-string 7))
  253. (setq seconds (string-to-number (match-string 8)))
  254. (and (string-equal (match-string 9) "min")
  255. (setq seconds (* 60 seconds)))
  256. (setq minutes (/ seconds 60)
  257. hours (/ seconds 3600))
  258. (setq remaining-time
  259. (format "%d:%02d" hours (- minutes (* 60 hours))))))))
  260. (list (cons ?v (or driver-version "N/A"))
  261. (cons ?V (or bios-version "N/A"))
  262. (cons ?I (or bios-interface "N/A"))
  263. (cons ?L (or line-status "N/A"))
  264. (cons ?B (or battery-status "N/A"))
  265. (cons ?b (or battery-status-symbol ""))
  266. (cons ?p (or load-percentage "N/A"))
  267. (cons ?s (or (and seconds (number-to-string seconds)) "N/A"))
  268. (cons ?m (or (and minutes (number-to-string minutes)) "N/A"))
  269. (cons ?h (or (and hours (number-to-string hours)) "N/A"))
  270. (cons ?t (or remaining-time "N/A")))))
  271. ;;; `/proc/acpi/' interface for Linux.
  272. (defun battery-linux-proc-acpi ()
  273. "Get ACPI status information from Linux (the kernel).
  274. This function works only with the `/proc/acpi/' format introduced
  275. in Linux version 2.4.20 and 2.6.0.
  276. The following %-sequences are provided:
  277. %c Current capacity (mAh)
  278. %r Current rate
  279. %B Battery status (verbose)
  280. %b Battery status, empty means high, `-' means low,
  281. `!' means critical, and `+' means charging
  282. %d Temperature (in degrees Celsius)
  283. %L AC line status (verbose)
  284. %p Battery load percentage
  285. %m Remaining time (to charge or discharge) in minutes
  286. %h Remaining time (to charge or discharge) in hours
  287. %t Remaining time (to charge or discharge) in the form `h:min'"
  288. (let ((design-capacity 0)
  289. (last-full-capacity 0)
  290. full-capacity
  291. (warn 0)
  292. (low 0)
  293. capacity rate rate-type charging-state minutes hours)
  294. ;; ACPI provides information about each battery present in the system in
  295. ;; a separate subdirectory. We are going to merge the available
  296. ;; information together since displaying for a variable amount of
  297. ;; batteries seems overkill for format-strings.
  298. (with-temp-buffer
  299. (dolist (dir (ignore-errors (directory-files "/proc/acpi/battery/"
  300. t "\\`[^.]")))
  301. (erase-buffer)
  302. (ignore-errors (insert-file-contents (expand-file-name "state" dir)))
  303. (when (re-search-forward "present: +yes$" nil t)
  304. (and (re-search-forward "charging state: +\\(.*\\)$" nil t)
  305. (member charging-state '("unknown" "charged" nil))
  306. ;; On most multi-battery systems, most of the time only one
  307. ;; battery is "charging"/"discharging", the others are
  308. ;; "unknown".
  309. (setq charging-state (match-string 1)))
  310. (when (re-search-forward "present rate: +\\([0-9]+\\) \\(m[AW]\\)$"
  311. nil t)
  312. (setq rate (+ (or rate 0) (string-to-number (match-string 1))))
  313. (when (> rate 0)
  314. (setq rate-type (or (and rate-type
  315. (if (string= rate-type (match-string 2))
  316. rate-type
  317. (error
  318. "Inconsistent rate types (%s vs. %s)"
  319. rate-type (match-string 2))))
  320. (match-string 2)))))
  321. (when (re-search-forward "remaining capacity: +\\([0-9]+\\) m[AW]h$"
  322. nil t)
  323. (setq capacity
  324. (+ (or capacity 0) (string-to-number (match-string 1))))))
  325. (goto-char (point-max))
  326. (ignore-errors (insert-file-contents (expand-file-name "info" dir)))
  327. (when (re-search-forward "present: +yes$" nil t)
  328. (when (re-search-forward "design capacity: +\\([0-9]+\\) m[AW]h$"
  329. nil t)
  330. (cl-incf design-capacity (string-to-number (match-string 1))))
  331. (when (re-search-forward "last full capacity: +\\([0-9]+\\) m[AW]h$"
  332. nil t)
  333. (cl-incf last-full-capacity (string-to-number (match-string 1))))
  334. (when (re-search-forward
  335. "design capacity warning: +\\([0-9]+\\) m[AW]h$" nil t)
  336. (cl-incf warn (string-to-number (match-string 1))))
  337. (when (re-search-forward "design capacity low: +\\([0-9]+\\) m[AW]h$"
  338. nil t)
  339. (cl-incf low (string-to-number (match-string 1)))))))
  340. (setq full-capacity (if (> last-full-capacity 0)
  341. last-full-capacity design-capacity))
  342. (and capacity rate
  343. (setq minutes (if (zerop rate) 0
  344. (floor (* (/ (float (if (string= charging-state
  345. "charging")
  346. (- full-capacity capacity)
  347. capacity))
  348. rate)
  349. 60)))
  350. hours (/ minutes 60)))
  351. (list (cons ?c (or (and capacity (number-to-string capacity)) "N/A"))
  352. (cons ?L (or (battery-search-for-one-match-in-files
  353. (mapcar (lambda (e) (concat e "/state"))
  354. (ignore-errors
  355. (directory-files "/proc/acpi/ac_adapter/"
  356. t "\\`[^.]")))
  357. "state: +\\(.*\\)$" 1)
  358. "N/A"))
  359. (cons ?d (or (battery-search-for-one-match-in-files
  360. (mapcar (lambda (e) (concat e "/temperature"))
  361. (ignore-errors
  362. (directory-files "/proc/acpi/thermal_zone/"
  363. t "\\`[^.]")))
  364. "temperature: +\\([0-9]+\\) C$" 1)
  365. "N/A"))
  366. (cons ?r (or (and rate (concat (number-to-string rate) " "
  367. rate-type)) "N/A"))
  368. (cons ?B (or charging-state "N/A"))
  369. (cons ?b (or (and (string= charging-state "charging") "+")
  370. (and capacity (< capacity low) "!")
  371. (and capacity (< capacity warn) "-")
  372. ""))
  373. (cons ?h (or (and hours (number-to-string hours)) "N/A"))
  374. (cons ?m (or (and minutes (number-to-string minutes)) "N/A"))
  375. (cons ?t (or (and minutes
  376. (format "%d:%02d" hours (- minutes (* 60 hours))))
  377. "N/A"))
  378. (cons ?p (or (and full-capacity capacity
  379. (> full-capacity 0)
  380. (number-to-string
  381. (floor (/ capacity
  382. (/ (float full-capacity) 100)))))
  383. "N/A")))))
  384. ;;; `/sys/class/power_supply/BATN' interface for Linux.
  385. (defun battery-linux-sysfs ()
  386. "Get ACPI status information from Linux kernel.
  387. This function works only with the new `/sys/class/power_supply/'
  388. format introduced in Linux version 2.4.25.
  389. The following %-sequences are provided:
  390. %c Current capacity (mAh or mWh)
  391. %r Current rate
  392. %B Battery status (verbose)
  393. %d Temperature (in degrees Celsius)
  394. %p Battery load percentage
  395. %L AC line status (verbose)
  396. %m Remaining time (to charge or discharge) in minutes
  397. %h Remaining time (to charge or discharge) in hours
  398. %t Remaining time (to charge or discharge) in the form `h:min'"
  399. (let (charging-state temperature hours
  400. ;; Some batteries report charges and current, other energy and power.
  401. ;; In order to reliably be able to combine those data, we convert them
  402. ;; all to energy/power (since we can't combine different charges if
  403. ;; they're not at the same voltage).
  404. (energy-full 0.0)
  405. (energy-now 0.0)
  406. (power-now 0.0)
  407. (voltage-now 10.8)) ;Arbitrary default, in case the info is missing.
  408. ;; SysFS provides information about each battery present in the
  409. ;; system in a separate subdirectory. We are going to merge the
  410. ;; available information together.
  411. (with-temp-buffer
  412. (dolist (dir (ignore-errors
  413. (directory-files
  414. "/sys/class/power_supply/" t
  415. battery-linux-sysfs-regexp)))
  416. (erase-buffer)
  417. (ignore-errors (insert-file-contents
  418. (expand-file-name "uevent" dir)))
  419. (goto-char (point-min))
  420. (when (re-search-forward
  421. "POWER_SUPPLY_VOLTAGE_NOW=\\([0-9]*\\)$" nil t)
  422. (setq voltage-now (/ (string-to-number (match-string 1)) 1000000.0)))
  423. (goto-char (point-min))
  424. (when (re-search-forward "POWER_SUPPLY_PRESENT=1$" nil t)
  425. (goto-char (point-min))
  426. (and (re-search-forward "POWER_SUPPLY_STATUS=\\(.*\\)$" nil t)
  427. (member charging-state '("Unknown" "Full" nil))
  428. (setq charging-state (match-string 1)))
  429. (goto-char (point-min))
  430. (when (re-search-forward
  431. "POWER_SUPPLY_\\(CURRENT\\|POWER\\)_NOW=\\([0-9]*\\)$"
  432. nil t)
  433. (cl-incf power-now
  434. (* (float (string-to-number (match-string 2)))
  435. (if (eq (char-after (match-beginning 1)) ?C)
  436. voltage-now 1.0))))
  437. (goto-char (point-min))
  438. (when (re-search-forward "POWER_SUPPLY_TEMP=\\([0-9]*\\)$" nil t)
  439. (setq temperature (match-string 1)))
  440. (goto-char (point-min))
  441. (let (full-string now-string)
  442. ;; Sysfs may list either charge (mAh) or energy (mWh).
  443. ;; Keep track of both, and choose which to report later.
  444. (cond ((and (re-search-forward
  445. "POWER_SUPPLY_CHARGE_FULL=\\([0-9]*\\)$" nil t)
  446. (setq full-string (match-string 1))
  447. (re-search-forward
  448. "POWER_SUPPLY_CHARGE_NOW=\\([0-9]*\\)$" nil t)
  449. (setq now-string (match-string 1)))
  450. (cl-incf energy-full (* (string-to-number full-string)
  451. voltage-now))
  452. (cl-incf energy-now (* (string-to-number now-string)
  453. voltage-now)))
  454. ((and (progn (goto-char (point-min)) t)
  455. (re-search-forward
  456. "POWER_SUPPLY_ENERGY_FULL=\\([0-9]*\\)$" nil t)
  457. (setq full-string (match-string 1))
  458. (re-search-forward
  459. "POWER_SUPPLY_ENERGY_NOW=\\([0-9]*\\)$" nil t)
  460. (setq now-string (match-string 1)))
  461. (cl-incf energy-full (string-to-number full-string))
  462. (cl-incf energy-now (string-to-number now-string)))))
  463. (goto-char (point-min))
  464. (unless (zerop power-now)
  465. (let ((remaining (if (string= charging-state "Discharging")
  466. energy-now
  467. (- energy-full energy-now))))
  468. (setq hours (/ remaining power-now)))))))
  469. (list (cons ?c (cond ((or (> energy-full 0) (> energy-now 0))
  470. (number-to-string (/ energy-now voltage-now)))
  471. (t "N/A")))
  472. (cons ?r (if (> power-now 0.0)
  473. (format "%.1f" (/ power-now 1000000.0))
  474. "N/A"))
  475. (cons ?m (if hours (format "%d" (* hours 60)) "N/A"))
  476. (cons ?h (if hours (format "%d" hours) "N/A"))
  477. (cons ?t (if hours
  478. (format "%d:%02d" hours (* (- hours (floor hours)) 60))
  479. "N/A"))
  480. (cons ?d (or temperature "N/A"))
  481. (cons ?B (or charging-state "N/A"))
  482. (cons ?p (cond ((and (> energy-full 0) (> energy-now 0))
  483. (format "%.1f"
  484. (/ (* 100 energy-now) energy-full)))
  485. (t "N/A")))
  486. (cons ?L (cond
  487. ((battery-search-for-one-match-in-files
  488. (list "/sys/class/power_supply/AC/online"
  489. "/sys/class/power_supply/ACAD/online"
  490. "/sys/class/power_supply/ADP1/online")
  491. "1" 0)
  492. "AC")
  493. ((battery-search-for-one-match-in-files
  494. (list "/sys/class/power_supply/AC/online"
  495. "/sys/class/power_supply/ACAD/online"
  496. "/sys/class/power_supply/ADP1/online")
  497. "0" 0)
  498. "BAT")
  499. (t "N/A"))))))
  500. (declare-function dbus-get-property "dbus.el"
  501. (bus service path interface property))
  502. ;;; `upowerd' interface.
  503. (defsubst battery-upower-prop (pname &optional device)
  504. (dbus-get-property
  505. :system
  506. "org.freedesktop.UPower"
  507. (concat "/org/freedesktop/UPower/devices/" (or device battery-upower-device))
  508. "org.freedesktop.UPower"
  509. pname))
  510. (defun battery-upower ()
  511. "Get battery status from dbus Upower interface.
  512. This function works only in systems with `upowerd' daemon
  513. running.
  514. The following %-sequences are provided:
  515. %c Current capacity (mWh)
  516. %p Battery load percentage
  517. %r Current rate
  518. %B Battery status (verbose)
  519. %L AC line status (verbose)
  520. %s Remaining time (to charge or discharge) in seconds
  521. %m Remaining time (to charge or discharge) in minutes
  522. %h Remaining time (to charge or discharge) in hours
  523. %t Remaining time (to charge or discharge) in the form `h:min'"
  524. (let ((percents (battery-upower-prop "Percentage"))
  525. (time-to-empty (battery-upower-prop "TimeToEmpty"))
  526. (time-to-full (battery-upower-prop "TimeToFull"))
  527. (state (battery-upower-prop "State"))
  528. (online (battery-upower-prop "Online" "line_power_ACAD"))
  529. (energy (battery-upower-prop "Energy"))
  530. (energy-rate (battery-upower-prop "EnergyRate"))
  531. (battery-states '((0 . "unknown") (1 . "charging")
  532. (2 . "discharging") (3 . "empty")
  533. (4 . "fully-charged") (5 . "pending-charge")
  534. (6 . "pending-discharge")))
  535. seconds minutes hours remaining-time)
  536. (cond ((and online time-to-full)
  537. (setq seconds time-to-full))
  538. ((and (not online) time-to-empty)
  539. (setq seconds time-to-empty)))
  540. (when seconds
  541. (setq minutes (/ seconds 60)
  542. hours (/ minutes 60)
  543. remaining-time
  544. (format "%d:%02d" (truncate hours)
  545. (- (truncate minutes) (* 60 (truncate hours))))))
  546. (list (cons ?c (or (and energy
  547. (number-to-string (round (* 1000 energy))))
  548. "N/A"))
  549. (cons ?p (or (and percents (number-to-string (round percents)))
  550. "N/A"))
  551. (cons ?r (or (and energy-rate
  552. (concat (number-to-string energy-rate) " W"))
  553. "N/A"))
  554. (cons ?B (or (and state (cdr (assoc state battery-states)))
  555. "unknown"))
  556. (cons ?L (or (and online "on-line") "off-line"))
  557. (cons ?s (or (and seconds (number-to-string seconds)) "N/A"))
  558. (cons ?m (or (and minutes (number-to-string minutes)) "N/A"))
  559. (cons ?h (or (and hours (number-to-string hours)) "N/A"))
  560. (cons ?t (or remaining-time "N/A")))))
  561. ;;; `apm' interface for BSD.
  562. (defun battery-bsd-apm ()
  563. "Get APM status information from BSD apm binary.
  564. The following %-sequences are provided:
  565. %L AC line status (verbose)
  566. %B Battery status (verbose)
  567. %b Battery status, empty means high, `-' means low,
  568. `!' means critical, and `+' means charging
  569. %P Advanced power saving mode state (verbose)
  570. %p Battery charge percentage
  571. %s Remaining battery charge time in seconds
  572. %m Remaining battery charge time in minutes
  573. %h Remaining battery charge time in hours
  574. %t Remaining battery charge time in the form `h:min'"
  575. (let* ((os-name (car (split-string
  576. (shell-command-to-string "/usr/bin/uname"))))
  577. (apm-flag (if (equal os-name "OpenBSD") "P" "s"))
  578. (apm-cmd (concat "/usr/sbin/apm -ablm" apm-flag))
  579. (apm-output (split-string (shell-command-to-string apm-cmd)))
  580. ;; Battery status
  581. (battery-status
  582. (let ((stat (string-to-number (nth 0 apm-output))))
  583. (cond ((eq stat 0) '("high" . ""))
  584. ((eq stat 1) '("low" . "-"))
  585. ((eq stat 2) '("critical" . "!"))
  586. ((eq stat 3) '("charging" . "+"))
  587. ((eq stat 4) '("absent" . nil)))))
  588. ;; Battery percentage
  589. (battery-percentage (nth 1 apm-output))
  590. ;; Battery life
  591. (battery-life (nth 2 apm-output))
  592. ;; AC status
  593. (line-status
  594. (let ((ac (string-to-number (nth 3 apm-output))))
  595. (cond ((eq ac 0) "disconnected")
  596. ((eq ac 1) "connected")
  597. ((eq ac 2) "backup power"))))
  598. ;; Advanced power savings mode
  599. (apm-mode
  600. (let ((apm (string-to-number (nth 4 apm-output))))
  601. (if (string= os-name "OpenBSD")
  602. (cond ((eq apm 0) "manual")
  603. ((eq apm 1) "automatic")
  604. ((eq apm 2) "cool running"))
  605. (if (eq apm 1) "on" "off"))))
  606. seconds minutes hours remaining-time)
  607. (unless (member battery-life '("unknown" "-1"))
  608. (if (member os-name '("OpenBSD" "NetBSD"))
  609. (setq minutes (string-to-number battery-life)
  610. seconds (* 60 minutes))
  611. (setq seconds (string-to-number battery-life)
  612. minutes (truncate (/ seconds 60))))
  613. (setq hours (truncate (/ minutes 60))
  614. remaining-time (format "%d:%02d" hours
  615. (- minutes (* 60 hours)))))
  616. (list (cons ?L (or line-status "N/A"))
  617. (cons ?B (or (car battery-status) "N/A"))
  618. (cons ?b (or (cdr battery-status) "N/A"))
  619. (cons ?p (if (string= battery-percentage "255")
  620. "N/A"
  621. battery-percentage))
  622. (cons ?P (or apm-mode "N/A"))
  623. (cons ?s (or (and seconds (number-to-string seconds)) "N/A"))
  624. (cons ?m (or (and minutes (number-to-string minutes)) "N/A"))
  625. (cons ?h (or (and hours (number-to-string hours)) "N/A"))
  626. (cons ?t (or remaining-time "N/A")))))
  627. ;;; `pmset' interface for Darwin (macOS).
  628. (defun battery-pmset ()
  629. "Get battery status information using `pmset'.
  630. The following %-sequences are provided:
  631. %L Power source (verbose)
  632. %B Battery status (verbose)
  633. %b Battery status, empty means high, `-' means low,
  634. `!' means critical, and `+' means charging
  635. %p Battery load percentage
  636. %h Remaining time in hours
  637. %m Remaining time in minutes
  638. %t Remaining time in the form `h:min'"
  639. (let (power-source load-percentage battery-status battery-status-symbol
  640. remaining-time hours minutes)
  641. (with-temp-buffer
  642. (ignore-errors (call-process "pmset" nil t nil "-g" "ps"))
  643. (goto-char (point-min))
  644. (when (re-search-forward "\\(?:Currentl?y\\|Now\\) drawing from '\\(AC\\|Battery\\) Power'" nil t)
  645. (setq power-source (match-string 1))
  646. (when (re-search-forward "^ -InternalBattery-0\\([ \t]+(id=[0-9]+)\\)*[ \t]+" nil t)
  647. (when (looking-at "\\([0-9]\\{1,3\\}\\)%")
  648. (setq load-percentage (match-string 1))
  649. (goto-char (match-end 0))
  650. (cond ((looking-at "; charging")
  651. (setq battery-status "charging"
  652. battery-status-symbol "+"))
  653. ((< (string-to-number load-percentage) battery-load-critical)
  654. (setq battery-status "critical"
  655. battery-status-symbol "!"))
  656. ((< (string-to-number load-percentage) battery-load-low)
  657. (setq battery-status "low"
  658. battery-status-symbol "-"))
  659. (t
  660. (setq battery-status "high"
  661. battery-status-symbol "")))
  662. (when (re-search-forward "\\(\\([0-9]+\\):\\([0-9]+\\)\\) remaining" nil t)
  663. (setq remaining-time (match-string 1))
  664. (let ((h (string-to-number (match-string 2)))
  665. (m (string-to-number (match-string 3))))
  666. (setq hours (number-to-string (+ h (if (< m 30) 0 1)))
  667. minutes (number-to-string (+ (* h 60) m)))))))))
  668. (list (cons ?L (or power-source "N/A"))
  669. (cons ?p (or load-percentage "N/A"))
  670. (cons ?B (or battery-status "N/A"))
  671. (cons ?b (or battery-status-symbol ""))
  672. (cons ?h (or hours "N/A"))
  673. (cons ?m (or minutes "N/A"))
  674. (cons ?t (or remaining-time "N/A")))))
  675. ;;; Private functions.
  676. (defun battery-format (format alist)
  677. "Substitute %-sequences in FORMAT."
  678. (replace-regexp-in-string
  679. "%."
  680. (lambda (str)
  681. (let ((char (aref str 1)))
  682. (if (eq char ?%) "%"
  683. (or (cdr (assoc char alist)) ""))))
  684. format t t))
  685. (defun battery-search-for-one-match-in-files (files regexp match-num)
  686. "Search REGEXP in the content of the files listed in FILES.
  687. If a match occurred, return the parenthesized expression numbered by
  688. MATCH-NUM in the match. Otherwise, return nil."
  689. (with-temp-buffer
  690. (catch 'found
  691. (dolist (file files)
  692. (and (ignore-errors (insert-file-contents file nil nil nil 'replace))
  693. (re-search-forward regexp nil t)
  694. (throw 'found (match-string match-num)))))))
  695. (provide 'battery)
  696. ;;; battery.el ends here