Skip to content

Commit d8f4317

Browse files
zevlgmonnier
authored andcommitted
Make 'M-x battery RET' work out-of-box for UPower users.
* battery.el (battery-upower-prop): Removed in favor for 'battery-upower-device-property'. (battery-upower-device): Can be nil, meaning autodetect the battery device. (battery-upower-line-power-device): New. line-power device. Can be nil, meaning autodetect line-power device. (battery-status-function): Check UPower service is available to use 'battery-upower' as status function. (battery-upower): Speedup. Request D-Bus only once, fetching all the properties at once. Provide string for "%b" format spec. (battery-upower-device-list, battery-upower-device-all-properties, battery-upower-device-property): New functions to work with UPower devices. (battery-upower-dbus-service, battery-upower-dbus-interface, battery-upower-dbus-path, battery-upower-dbus-device-interface, battery-upower-dbus-device-path): New constants describing UPower D-Bus service.
1 parent 7e0a4b7 commit d8f4317

File tree

1 file changed

+124
-52
lines changed

1 file changed

+124
-52
lines changed

lisp/battery.el

Lines changed: 124 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -23,27 +23,42 @@
2323
;;; Commentary:
2424

2525
;; There is at present support for GNU/Linux, macOS 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 (macOS) is supported by using the `pmset'
29-
;; program. Windows is supported by the GetSystemPowerStatus API call.
26+
;; library supports UPower (https://upower.freedesktop.org) via D-Bus
27+
;; API or the `/proc/apm' file format of Linux version 1.3.58 or newer
28+
;; and the `/proc/acpi/' directory structure of Linux 2.4.20 and 2.6.
29+
;; Darwin (macOS) is supported by using the `pmset' program. Windows
30+
;; is supported by the GetSystemPowerStatus API call.
3031

3132
;;; Code:
3233

3334
(require 'timer)
35+
(require 'dbus)
3436
(eval-when-compile (require 'cl-lib))
3537

3638
(defgroup battery nil
3739
"Display battery status information."
3840
:prefix "battery-"
3941
:group 'hardware)
4042

41-
(defcustom battery-upower-device "battery_BAT1"
42-
"Upower battery device name."
43-
:version "26.1"
44-
:type 'string
43+
(defcustom battery-upower-device nil
44+
"UPower device of the `:battery' type.
45+
Use `battery-upower-device-list' to list all available UPower devices.
46+
If set to nil, then autodetect `:battery' device."
47+
:version "28.1"
48+
:type '(choice string (const :tag "Autodetect" nil))
4549
:group 'battery)
4650

51+
(defcustom battery-upower-line-power-device nil
52+
"UPower device of the `:line-power' type.
53+
Use `battery-upower-device-list' to list all available UPower devices.
54+
If set to nil, then autodetect `:battery' device."
55+
:version "28.1"
56+
:type '(choice string (const :tag "Autodetect" nil))
57+
:group 'battery)
58+
59+
(defconst battery-upower-dbus-service "org.freedesktop.UPower"
60+
"Well-known UPower service name for the D-Bus system.")
61+
4762
(defun battery--find-linux-sysfs-batteries ()
4863
(let ((dirs nil))
4964
(dolist (file (directory-files "/sys/class/power_supply/" t))
@@ -54,7 +69,9 @@
5469
(nreverse dirs)))
5570

5671
(defcustom battery-status-function
57-
(cond ((and (eq system-type 'gnu/linux)
72+
(cond ((dbus-ping :system battery-upower-dbus-service)
73+
#'battery-upower)
74+
((and (eq system-type 'gnu/linux)
5875
(file-readable-p "/proc/apm"))
5976
#'battery-linux-proc-apm)
6077
((and (eq system-type 'gnu/linux)
@@ -537,17 +554,68 @@ The following %-sequences are provided:
537554
(t "N/A"))))))
538555

539556

540-
(declare-function dbus-get-property "dbus.el"
541-
(bus service path interface property))
542-
543557
;;; `upowerd' interface.
544-
(defsubst battery-upower-prop (pname &optional device)
558+
(defconst battery-upower-dbus-interface "org.freedesktop.UPower"
559+
"The interface to UPower.
560+
See URL `https://upower.freedesktop.org/docs/'.")
561+
562+
(defconst battery-upower-dbus-path "/org/freedesktop/UPower"
563+
"D-Bus path to talk to UPower service.")
564+
565+
(defconst battery-upower-dbus-device-interface
566+
(concat battery-upower-dbus-interface ".Device")
567+
"The Device interface of the UPower.
568+
See URL `https://upower.freedesktop.org/docs/Device.html'.")
569+
570+
(defconst battery-upower-dbus-device-path
571+
(concat battery-upower-dbus-path "/devices")
572+
"D-Bus path to talk to devices part of the UPower service.")
573+
574+
(defconst battery-upower-types
575+
'((0 . :unknown) (1 . :line-power) (2 . :battery)
576+
(3 . :ups) (4 . :monitor) (5 . :mouse)
577+
(6 . :keyboard) (7 . :pda) (8 . :phone))
578+
"Type of the device.")
579+
580+
(defconst battery-upower-states
581+
'((0 . "unknown") (1 . "charging") (2 . "discharging")
582+
(3 . "empty") (4 . "fully-charged") (5 . "pending-charge")
583+
(6 . "pending-discharge"))
584+
"Alist of battery power states.
585+
Only valid for `:battery' devices.")
586+
587+
(defun battery-upower-device-property (device property)
588+
"Get value of the single PROPERTY for the UPower DEVICE."
545589
(dbus-get-property
546-
:system
547-
"org.freedesktop.UPower"
548-
(concat "/org/freedesktop/UPower/devices/" (or device battery-upower-device))
549-
"org.freedesktop.UPower"
550-
pname))
590+
:system battery-upower-dbus-service
591+
(expand-file-name device battery-upower-dbus-device-path)
592+
battery-upower-dbus-device-interface
593+
property))
594+
595+
(defun battery-upower-device-all-properties (device)
596+
"Return value for all available properties for the UPower DEVICE."
597+
(dbus-get-all-properties
598+
:system battery-upower-dbus-service
599+
(expand-file-name device battery-upower-dbus-device-path)
600+
battery-upower-dbus-device-interface))
601+
602+
(defun battery-upower-device-list ()
603+
"Return list of all available UPower devices.
604+
Each element is the cons cell in form: (DEVICE . DEVICE-TYPE)."
605+
(mapcar (lambda (device-path)
606+
(let* ((device (file-relative-name
607+
device-path battery-upower-dbus-device-path))
608+
(type-num (battery-upower-device-property device "Type")))
609+
(cons device (or (cdr (assq type-num battery-upower-types))
610+
:unknown))))
611+
(dbus-call-method :system battery-upower-dbus-service
612+
battery-upower-dbus-path
613+
battery-upower-dbus-interface
614+
"EnumerateDevices")))
615+
616+
(defun battery-upower-device-autodetect (device-type)
617+
"Return first matching UPower device of DEVICE-TYPE."
618+
(car (rassq device-type (battery-upower-device-list))))
551619

552620
(defun battery-upower ()
553621
"Get battery status from dbus Upower interface.
@@ -559,45 +627,49 @@ The following %-sequences are provided:
559627
%p Battery load percentage
560628
%r Current rate
561629
%B Battery status (verbose)
630+
%b Battery status: empty means high, `-' means low,
631+
`!' means critical, and `+' means charging
562632
%L AC line status (verbose)
563633
%s Remaining time (to charge or discharge) in seconds
564634
%m Remaining time (to charge or discharge) in minutes
565635
%h Remaining time (to charge or discharge) in hours
566636
%t Remaining time (to charge or discharge) in the form `h:min'"
567-
(let ((percents (battery-upower-prop "Percentage"))
568-
(time-to-empty (battery-upower-prop "TimeToEmpty"))
569-
(time-to-full (battery-upower-prop "TimeToFull"))
570-
(state (battery-upower-prop "State"))
571-
(online (battery-upower-prop "Online" "line_power_ACAD"))
572-
(energy (battery-upower-prop "Energy"))
573-
(energy-rate (battery-upower-prop "EnergyRate"))
574-
(battery-states '((0 . "unknown") (1 . "charging")
575-
(2 . "discharging") (3 . "empty")
576-
(4 . "fully-charged") (5 . "pending-charge")
577-
(6 . "pending-discharge")))
578-
seconds minutes hours remaining-time)
579-
(cond ((and online time-to-full)
580-
(setq seconds time-to-full))
581-
((and (not online) time-to-empty)
582-
(setq seconds time-to-empty)))
583-
(when seconds
584-
(setq minutes (/ seconds 60)
585-
hours (/ minutes 60)
586-
remaining-time (format "%d:%02d" hours (mod minutes 60))))
587-
(list (cons ?c (or (and energy
588-
(number-to-string (round (* 1000 energy))))
589-
"N/A"))
590-
(cons ?p (or (and percents (number-to-string (round percents)))
591-
"N/A"))
592-
(cons ?r (or (and energy-rate
593-
(concat (number-to-string energy-rate) " W"))
594-
"N/A"))
595-
(cons ?B (or (and state (cdr (assoc state battery-states)))
596-
"unknown"))
597-
(cons ?L (or (and online "on-line") "off-line"))
598-
(cons ?s (or (and seconds (number-to-string seconds)) "N/A"))
599-
(cons ?m (or (and minutes (number-to-string minutes)) "N/A"))
600-
(cons ?h (or (and hours (number-to-string hours)) "N/A"))
637+
(let* ((bat-device (or battery-upower-device
638+
(battery-upower-device-autodetect :battery)))
639+
(bat-props (when bat-device
640+
(battery-upower-device-all-properties bat-device)))
641+
(percents (cdr (assoc "Percentage" bat-props)))
642+
(time-to-empty (cdr (assoc "TimeToEmpty" bat-props)))
643+
(time-to-full (cdr (assoc "TimeToFull" bat-props)))
644+
(state (cdr (assoc "State" bat-props)))
645+
(level (cdr (assoc "BatteryLevel" bat-props)))
646+
(energy (cdr (assoc "Energy" bat-props)))
647+
(energy-rate (cdr (assoc "EnergyRate" bat-props)))
648+
(lp-device (or battery-upower-line-power-device
649+
(battery-upower-device-autodetect :line-power)))
650+
(online-p (when lp-device
651+
(battery-upower-device-property lp-device "Online")))
652+
(seconds (if online-p time-to-full time-to-empty))
653+
(minutes (when seconds (/ seconds 60)))
654+
(hours (when minutes (/ minutes 60)))
655+
(remaining-time (when hours
656+
(format "%d:%02d" hours (mod minutes 60)))))
657+
(list (cons ?c (if energy (number-to-string (round (* 1000 energy))) "N/A"))
658+
(cons ?p (if percents (number-to-string (round percents)) "N/A"))
659+
(cons ?r (if energy-rate
660+
(concat (number-to-string energy-rate) " W")
661+
"N/A"))
662+
(cons ?B (if state
663+
(cdr (assq state battery-upower-states))
664+
"unknown"))
665+
(cons ?b (cond ((= level 3) "-")
666+
((= level 4) "!")
667+
(online-p "+")
668+
(t "")))
669+
(cons ?L (if online-p "on-line" (if lp-device "off-line" "unknown")))
670+
(cons ?s (if seconds (number-to-string seconds) "N/A"))
671+
(cons ?m (if minutes (number-to-string minutes) "N/A"))
672+
(cons ?h (if hours (number-to-string hours) "N/A"))
601673
(cons ?t (or remaining-time "N/A")))))
602674

603675

0 commit comments

Comments
 (0)